简体   繁体   中英

XSLT copy, apply-templates select=“@*|node()” only copies node values

I want to copy nodes from the source to the output if XML matches on the root node. I tried to use:

<xsl:stylesheet version="1.0" xmlns:i="http://www.somestuff.com xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="xml"/>
 <xsl:template match="/i:Root">
  <xsl:copy>
   <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
 </xsl:template>
</xsl:stylesheet>

With this input xml, I match on the root node but the output only picks up the values of the nodes.

<?xml version="1.0" encoding="utf-8"?>
<i:Root xmlns:i="http://www.somestuff.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>
 <i:Info>
    <i:Num1>1234567890</i:Num1>
    <i:Num2>1234567890</i:Num2>
    <i:Service> code="1">String</i:Service>
    <i:Type code="0">String</i:Type>
    <i:Source>V</i:Source>
    <i:MainNum>1234567890</i:MainNum>
    <i:Name>a</i:Name>
    <i:Code>a</i:Code>
    <i:AttentionIndicator code="1">String</i:AttentionIndicator>
    <i:SpecialMessage>a</i:SpecialMessage>
    <i:AdditionalInfo>a</i:AdditionalInfo>
 </i:Info>
</i:Root>

Output:

<?xml version="1.0" encoding="utf-8"?><i:Root xmlns:i="http://www.somestuff.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    1234567890
    1234567890
    String
    String
    V
    1234567890
    a
    a
    String
    a
    a
</i:Root>

This seems like I'm missing something pretty simple like the apply-templates not working the way I think. Thanks for the help!

I would like to get straight a copy of the input XML if the root node matches i:Root.

Then, a simple identity transform as in Kevin's answer will not help because the document is copied regardless of whether the outermost node is called i:Root or not. Why? Because even if the template that matches /i:Root is not invoked in this case, the other one is.

Use the xsl:copy-of element to effectively copy everything, after checking the name of the document element.

Stylesheet

<xsl:stylesheet version="1.0" xmlns:i="http://www.somestuff.com"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*">
  <xsl:if test="self::i:Root">
    <xsl:copy>
      <xsl:copy-of select="@*|node()"/>
    </xsl:copy>
  </xsl:if>
 </xsl:template>


</xsl:stylesheet>

Now, if we change the XML Input to:

<?xml version="1.0" encoding="utf-8"?>
<i:SomethingElse xmlns:i="http://www.somestuff.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <i:Info>
    <i:Num1>1234567890</i:Num1>
    <i:Num2>1234567890</i:Num2>
    <i:Service> code="1">String</i:Service>
    <i:Type code="0">String</i:Type>
    <i:Source>V</i:Source>
    <i:MainNum>1234567890</i:MainNum>
    <i:Name>a</i:Name>
    <i:Code>a</i:Code>
    <i:AttentionIndicator code="1">String</i:AttentionIndicator>
    <i:SpecialMessage>a</i:SpecialMessage>
    <i:AdditionalInfo>a</i:AdditionalInfo>
 </i:Info>
</i:SomethingElse>

We get the following output, as expected:

<?xml version="1.0" encoding="utf-8"?>

Otherwise (that is, if the document element is called i:Root ), the output XML is an exact copy of the input. Try different inputs online yourself, here .

Yes your missing something, but there is nothing wrong with your xsl:apply-template or select for that matter.

You're missing a template for the other nodes besides i:Root, similar to this:

<xsl:stylesheet version="1.0" xmlns:i="http://www.somestuff.com" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:template match="/i:Root">
        <xsl:copy>
            <xsl:apply-templates select="node() | @*"/>
        </xsl:copy>
    </xsl:template>

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

If you don't define a template, the xslt processor falls back to it's built-in templates.

The built-in template will for nodes:

<xsl:template match="*|/">
  <xsl:apply-templates/>
</xsl:template>

Combined with the built-in template for text nodes:

<xsl:template match="text( )|@*">
  <xsl:value-of select="."/>
</xsl:template>

... only your text is displayed.


Edit: just read your comment on getting a straight copy:

<xsl:stylesheet version="1.0" xmlns:i="http://www.somestuff.com" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:template match="/i:Root">
        <xsl:copy-of select="."/>
    </xsl:template>
    <xsl:template match="/*" priority="-1">
        <Empty/><!-- or sth creative, but valid xml should have a root node -->
    </xsl:template>
</xsl:stylesheet>

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