仅选择包含特定子节点的节点,并将它们格式化为YAML

时间:2012-06-25 16:05:15

标签: xslt yaml

这一直是我的头脑。你能帮我吗?! 我有一个简单的xml文件。使用xslt我想将其转换为yaml文件。问题是:

  1. 我需要以一种方式组织输出,我只能从xml标签中捕获 specyfic值并将它们放到specyfic yaml词典(工作,地址)
  2. 某些xml标签会有名字()或者attrs()i 无法提前预测 - 因此我不能只通过names()或position()或其他逻辑来获取它们。我可以做一些假设但不是所有标签...
  3. 在格式化输出时,我发现在我选择了我想要的值之后,为yaml文件设置字典键(工作,地址)真的很难 - 我可以为所有孩子设置密钥ofert(我不需要)或者我可以为每个单独的元素设置关键字,这不是我想要的。
  4. 考虑一下:

    <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>
    

    我想输出如下:

    data:
        info: ....
        data: ....
            list:
                ofert:
                    id: 00934
                    name: Bob
                    address: {street: Euston rd, postcode: SE23GH }
                    work: {job: IT, position: boss }
                    other: {unknow: ....., unknow1: .... }
    
    
    

1 个答案:

答案 0 :(得分:1)

这是一个简单的XSLT 1.0解决方案,不包含'other:'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约束,请告诉我们。

如果我有时间,我会更新解决方案以包含'other:'元素。基本上,该技术只是定义一个命名模板来生成关联数组,如address:,work:和other:,其中输入参数包括缩进级别,标签('地址'等)和关联数组成员列表节点。我希望你明白这一点。


更新

这是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>