0

这已经让我头疼了一整天。你能帮我吗?!我有一个简单的 xml 文件。使用 xslt 我想将其转换为 yaml 文件。问题是:

  1. 我需要以一种我应该只从 xml 标签中捕获特定值 并将它们放入特定 yaml 字典(工作、地址)的方式组织输出
  2. 一些 xml 标记将具有我无法提前预测的names() 或 attrs() - 因此我不能仅通过 names() 或 position() 或其他一些逻辑来获取它们。我可以做出一些假设,但不是针对所有标签...
  3. 在格式化输出时,我发现在我选择了我想要的值之后,很难为 yaml 文件设置字典键(工作、地址) - 我可以为所有的孩子设置键(我不需要)或我可以为每个单独的元素设置键,这不是我想要的。

考虑一下:

<data>
   <info>some info</info>
   <data>some data</data>
   <list>
         <ofert>
            <unknow>....</unknow>
            <unknow1>....</unknow1>
            <id>00934</id>
            <name>Bob</name>
            <street>Euston rd.</street>
            <postcode>SE23GH</postcode>
            <job>IT</job>
            <position>boss</position>
            <unknow>....</unknow>
            <unknow4>....</unknow4>
            <unknow2>....</unknow2>
            <unknow3>....</unknow3>
         </ofert>
   </list>
</data>

我想要这样的输出:

数据:
    资料:……
    数据: ....
        列表:
            提醒:
                编号:00934
                姓名:鲍勃
                地址:{街道:尤斯顿路,邮编:SE23GH}
                工作:{工作:IT,职位:老板}
                其他:{unknow:.....,unknow1:....}


4

1 回答 1

1

这是一个简单的 XSLT 1.0 解决方案,它不包含“其他:”yaml 元素...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<xsl:template match="@*" />

<xsl:template match="*">
  <xsl:call-template name="indent">
   <xsl:with-param name="spaces" select="count(ancestor::*)"/>
  </xsl:call-template>
  <xsl:value-of select="concat( local-name(), ': ', text(), '&#x0A;')"/>
  <xsl:apply-templates select="*" />
</xsl:template>

<xsl:template match="ofert">
  <xsl:variable name="indent" select="count(ancestor::*)" />
  <xsl:call-template name="indent">
   <xsl:with-param name="spaces" select="$indent"/>
  </xsl:call-template>
  <xsl:value-of select="concat( local-name(), ': ', text(), '&#x0A;')"/>
  <xsl:apply-templates select="id" />
  <xsl:apply-templates select="name" />
  <xsl:call-template name="indent">
   <xsl:with-param name="spaces" select="$indent + 1"/>
  </xsl:call-template>
  <xsl:value-of select="concat(
    'address: {street: ', street/text(),
    ', postcode: ', postcode,' }&#x0A;')"/>
  <xsl:call-template name="indent">
   <xsl:with-param name="spaces" select="$indent + 1"/>
  </xsl:call-template>
  <xsl:value-of select="concat(
     'work: {job: ', job,
  ', position: ', position, ' }&#x0A;')"/>
</xsl:template>


<xsl:template name="indent">
  <xsl:param name="spaces" />
  <xsl:if test="$spaces > 0">
   <xsl:value-of select="' '"/>
   <xsl:call-template name="indent">
    <xsl:with-param name="spaces" select="$spaces - 1"/>
   </xsl:call-template>
  </xsl:if>
</xsl:template>

</xsl:stylesheet>

XSLT 2.0 解决方案会简单得多。如果您不受 XSLT 1.0 的限制,请告诉我们。

如果有时间,我将更新解决方案以包含“其他:”元素。基本上,该技术将简单地定义一个命名模板来生成关联数组,如地址:、工作:和其他:,其中输入参数包括缩进级别、标签(“地址”等)和关联数组成员列表节点。我希望你能明白。


更新

这是 XSLT 2.0 版本,其中包括“其他”yaml 元素...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:zzart="http://stackoverflow.com/questions/11192960">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="indent-unit" select = "'  '" />

<xsl:function name="zzart:indent" as="xs:string">
 <xsl:param name="level" as="xs:integer"/>
 <xsl:value-of select="fn:string-join( for $in in 1 to $level return $indent-unit, '')" />
</xsl:function>

<xsl:template match="@*" />

<xsl:template match="*">
  <xsl:value-of select="concat( zzart:indent( count(ancestor::*)), local-name(), ': ', text(), '&#x0A;')"/>
  <xsl:apply-templates select="*" />
</xsl:template>

<xsl:template match="ofert">
  <xsl:variable name="level" select="count(ancestor::*)" />
  <xsl:variable name="indent" select="zzart:indent( $level)" />
  <xsl:value-of select="concat( $indent, local-name(), ': ', text(), '&#x0A;')"/>
  <xsl:apply-templates select="id" />
  <xsl:apply-templates select="name" />
  <xsl:call-template name="associative-array">
   <xsl:with-param name="level" select="$level + 1" />
   <xsl:with-param name="array-name" select="'address'" />
   <xsl:with-param name="array-members" select="(street, postcode)" />
  </xsl:call-template>
  <xsl:call-template name="associative-array">
   <xsl:with-param name="level" select="$level + 1" />
   <xsl:with-param name="array-name" select="'work'" />
   <xsl:with-param name="array-members" select="(job, position)" />
  </xsl:call-template>

  <xsl:call-template name="associative-array">
   <xsl:with-param name="level" select="$level + 1" />
   <xsl:with-param name="array-name" select="'other'" />
   <xsl:with-param name="array-members" select="*[not(self::id)][not(self::name)]
                                                 [not(self::street)][not(self::postcode)]
                                                 [not(self::job)][not(self::position)]" />
  </xsl:call-template>

</xsl:template>

<xsl:template name="associative-array">
 <xsl:param name="level" as="xs:integer" />
 <xsl:param name="array-name" as="xs:string" />
 <xsl:param name="array-members"/>
 <xsl:value-of select="concat( zzart:indent( $level), $array-name, ': {')" />
 <xsl:for-each select="$array-members">
  <xsl:value-of select="concat( local-name(), ': ', text(), if (position() != last()) then ', ' else ' }&#x0A;')" />
 </xsl:for-each>    
</xsl:template>

</xsl:stylesheet>
于 2012-06-25T17:12:13.097 回答