简体   繁体   中英

XML to XML conversion: nesting similar nodes

I have the following xml file:

<ref>
<m-citation>
<string-name>
<surname>Carey</surname>, <given-names>J.W.</given-names>
</string-name>, <string-name>
<surname>Wigand</surname>, <given-names>M.</given-names>
</string-name>, <string-name>
<surname>Can</surname>, <given-names>S.J.</given-names>
</string-name>,
<year>2007</year>. <source>xyz</source>, <volume>1</volume>, <f>75</f>-<l>85</l>. 
</m-citation>
</ref>

Need to convert this to:

<ref>
<m-citation>
<publisher>
<name>
<surname>Carey</surname><given-names>J.W.</given-names>
</name>, <name>
<surname>Wigand</surname><given-names>M.</given-names>
</name>, <name>
<surname>Can</surname><given-names>S.J.</given-names>
</name>,
</publisher> 
<year>2007</year>. <source>xyz</source>, <volume>1</volume>, <f>75</f>-<l>85</l>. 
</m-citation>
</ref>

Please note: 'string-name' changes to 'name', all 'string-name' elements are nested within 'publisher' element and comma after surname is not retained. Tried different ways but couldn't get the exact output. Help of any kind would be truly appreciated. Thanks.

I have written the following code:

<xsl:template match="m-citation">
<xsl:element name="m-citation">

<xsl:element name="publisher">
<xsl:for-each select="string-name[position() &lt;= last()]">
<xsl:element name="name">
<xsl:element name="surname">
<xsl:value-of select="surname"/>
</xsl:element>
<xsl:element name="given-names">
<xsl:value-of select="given-names"/>
</xsl:element>
</xsl:element><xsl:text>, </xsl:text>
</xsl:for-each>
</xsl:element>

<xsl:apply-templates/>

</xsl:element>
</xsl:template>


<xsl:template match="ref//year">
<xsl:element name="year">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

<xsl:template match="ref//source">
<xsl:element name="source">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

Similarly for elements volume, f and l.

I am getting the following output:

<ref>
<m-citation>
<publisher>
<name>
<surname>Carey</surname><given-names>J.W.</given-names>
</name>, <name>
<surname>Wigand</surname><given-names>M.</given-names>
</name>, <name>
<surname>Can</surname><given-names>S.J.</given-names>
</name>,
</publisher> ,,,
<year>2007</year>. <source>xyz</source>, <volume>1</volume>, <f>75</f>-<l>85</l>. 
</m-citation>
</ref>

as I am using apply-templates the commas after string-name (which are outside all child elements) get displayed (,,,). Sorry for the errors if any in the above lines of code. Have cut-shortened it here and there.

With the next XSLT the element string-name will be translated into name and a publisher element will be added. All other elements and values are copied:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="m-citation">
        <xsl:copy>
            <xsl:apply-templates select="@*" />

            <publisher>
                <xsl:apply-templates select="string-name" />
            </publisher>

            <xsl:apply-templates select="node()[not(self::string-name)]" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="string-name">
        <name>
          <xsl:apply-templates select="@*|node()" />
        </name>
    </xsl:template>
</xsl:stylesheet>

This XSLT stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

  <!-- The identity transform. -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- At m-citation, put in the publisher element then apply templates to all string-name
       elements, and to all text nodes that have a string-name element after them.
       Outside the publisher element, apply templates to everything else. -->
  <xsl:template match="m-citation">
    <xsl:copy>
      <publisher>
        <xsl:apply-templates select="string-name | text()[count(following-sibling::string-name) &gt; 0]"/>
      </publisher>
      <xsl:apply-templates select="node()[not(self::string-name) and count(following-sibling::string-name) = 0]"/>
    </xsl:copy>
  </xsl:template>

  <!-- Replace string-name with name then apply-templates to its children. -->
  <xsl:template match="string-name">
    <name>
      <xsl:apply-templates select="@*|node()"/>
    </name>
  </xsl:template>

  <!-- Remove the first text node that comes after surname (a comma in your particular
       input. -->
  <xsl:template match="text()[preceding-sibling::*[1][self::surname]]"/>

</xsl:stylesheet>

when applied to the following input XML:

<ref>
  <m-citation>
    <string-name>
      <surname>Carey</surname>, <given-names>J.W.</given-names>
    </string-name>, <string-name>
      <surname>Wigand</surname>, <given-names>M.</given-names>
    </string-name>, <string-name>
      <surname>Can</surname>, <given-names>S.J.</given-names>
    </string-name>
    <year>2007</year>. <source>xyz</source> <volume>1</volume> <f>75</f> <l>85</l>.
    </m-citation>
</ref>

produces the following output:

<ref>
  <m-citation><publisher>
    <name>
      <surname>Carey</surname><given-names>J.W.</given-names>
    </name>, <name>
      <surname>Wigand</surname><given-names>M.</given-names>
    </name>, <name>
      <surname>Can</surname><given-names>S.J.</given-names>
    </name></publisher>
    <source>xyz</source> <volume>1</volume> <f>75</f> <l>85</l>.
  </m-citation>
</ref>

which is not quite formatted as you want, but I think the content is all there correctly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM