Reputation: 23
I have been trying to move an attribute from one element to another but instead it creates another child element.
I have the following
<wrapper>
<Person ID="1">
<Person InfoName="bob" Gender="male" />
<Purchase Reference = "1" Item="book"/>
<Purchase Reference = "2" Item="shoes"/>
</Person>
<Person ID="2">
<Person InfoName="Jane" Gender="female"/>
<Purchase Reference = "1" Item="pen"/>
<Purchase Reference = "2" Item="hat"/>
</Person>
</wrapper>
and I need to have the following (I'm not precious about keeping as all the records will have the ID):
<wrapper>
<Person ID="1">
<Person InfoName="bob" Gender="male" ID = "1"/>
<Purchase Reference = "1" Item="book" ID="1"/>
<Purchase Reference = "2" Item="shoes" ID="1"/>
</Person>
<Person ID="2">
<Person InfoName="Jane" Gender="female" ID="2"/>
<Purchase Reference = "1" Item="pen" ID="2"/>
<Purchase Reference = "2" Item="hat" ID="2"/>
</Person>
</wrapper>
I have tried several different XSLTs with varying degrees of success. The following creates a new child node.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[@ID]">
<xsl:copy>
<xsl:apply-templates select="@*[name() != 'ID']" />
<xsl:element name="{name()}ID">
<xsl:value-of select="@ID" />
</xsl:element>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
Upvotes: 2
Views: 247
Reputation: 243529
This is the only solution so far, which places the ID
attribute last -- as wanted:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Person/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:apply-templates select="../@ID"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document:
<wrapper>
<Person ID="1">
<Person InfoName="bob" Gender="male" />
<Purchase Reference = "1" Item="book"/>
<Purchase Reference = "2" Item="shoes"/>
</Person>
<Person ID="2">
<Person InfoName="Jane" Gender="female"/>
<Purchase Reference = "1" Item="pen"/>
<Purchase Reference = "2" Item="hat"/>
</Person>
</wrapper>
The exact (not like the results from other solutions) wanted result is produced:
<wrapper>
<Person ID="1">
<Person InfoName="bob" Gender="male" ID="1"/>
<Purchase Reference="1" Item="book" ID="1"/>
<Purchase Reference="2" Item="shoes" ID="1"/>
</Person>
<Person ID="2">
<Person InfoName="Jane" Gender="female" ID="2"/>
<Purchase Reference="1" Item="pen" ID="2"/>
<Purchase Reference="2" Item="hat" ID="2"/>
</Person>
</wrapper>
Upvotes: 0
Reputation: 2167
(1) Take care of a valid xml. Attributes must be Key-Value-pair e.g. info="a"
(value must be in "
). XML is case-sensitiv.
(2) Your solution is much simpler:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="wrapper/Person/*">
<xsl:copy>
<xsl:apply-templates select="@* | ../@ID"/>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Select your elements to copy into via wrapper/Person/*
and grep the attribute ID
of parent via ..
.
Upvotes: 1
Reputation: 117083
Try it this way:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Person/*">
<xsl:copy>
<xsl:apply-templates select="@*|../@ID|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to the following well-formed input:
<Wrapper>
<Person ID ="1">
<Person Name="bob" Gender="male" />
<Purchase Reference = "1" Item="book"/>
<Purchase Reference = "2" Item="shoes"/>
</Person>
<Person ID ="2">
<Person Name="Jane" Gender="female"/>
<Purchase Reference = "1" Item="pen"/>
<Purchase Reference = "2" Item="hat"/>
</Person>
</Wrapper>
the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Wrapper>
<Person ID="1">
<Person ID="1" Name="bob" Gender="male"/>
<Purchase ID="1" Reference="1" Item="book"/>
<Purchase ID="1" Reference="2" Item="shoes"/>
</Person>
<Person ID="2">
<Person ID="2" Name="Jane" Gender="female"/>
<Purchase ID="2" Reference="1" Item="pen"/>
<Purchase ID="2" Reference="2" Item="hat"/>
</Person>
</Wrapper>
Upvotes: 0