How to duplicate xml elements

I need to duplicate the xml payload on so many useful xml payloads based on a specific identifier, e.g. userid

<ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> <ns2:UserId>237</ns2:UserId> </ns2:Details> 

I need a conclusion like

 <ns2:Details> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> </ns2:Details> <ns2:Details> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>237</ns2:UserId> </ns2:Details> 

it's possible


Update: The answer below works fine, but there is a small catch that I did not mention. If the user ID is the same and it repeats, then the same xml payload should be displayed. For this, I tried the following to get unique userid elements

 <xsl:param name="userId" select="ns0:UserId[generate-id(.)=generate-id(key('k', ns0:UserId)[1])]"/> 

but this does not work, and also tried to use the above

 ..[generate-id(.)=generate-id(key('k', ns0:UserId)[1])] 

at the template level also does not work

Am I missing something?

Update : I made a small modification of the above code, instead of working with xsl: param, I used it in xsl: apply-template

before the change (provided as an answer to me) <xsl: apply-templates select = "// ns2: Details / ns2: UserId" / "> after the modification <xsl: apply-templates select =" // ns2: Details / ns2 : UserId [generate-id (.) = Generate-id (key ('myUserId' ,.) [1])] "/">

In my error, I used ns2: userid instead of "."

full xsl code ---

<xsl: output method = "xml" indent = "yes" / "> <xsl: key name =" k "match =" ns2: UserId "use =" text () "/"> <xsl: key name = " myUserId "match =" ns2: UserId "use =". " / "> <xsl: template match =" / "> <NS2: Root> <xsl: apply-templates select =" // ns2: Details / ns2: UserId [generate-id (.) = generate-id (key ( 'myUserId' ,.) [1])] "/"> </ NS2: Root> </ XSL: pattern>

<xsl: template match = "// ns2: Details"> <xsl: param name = "userId" select = "ns2: UserId" / "> <NS2: Details> <xsl: copy-of select =" key (' k ', $ userId) [1] "/"> <! - displays values ​​UserId β†’ <xsl: copy-of select = "./* [name ()! = 'ns2: UserId']" / "> <! - displays other values ​​→ </ NS2: Details> </ XSL: template>

<xsl: template match = "ns2: UserId"> <xsl: apply-templates select = ".."> <xsl: with-param name = "userId" select = "." / "> </ XSL: templates apply-> </ XSL: templates>

Please confirm it. this works for me too ...

+6
source share
6 answers

Suggested XML:

 <ns2:Details xmlns:ns2="ns2"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> <ns2:UserId>237</ns2:UserId> <ns2:UserId>46</ns2:UserId> </ns2:Details> 

XSLT:

 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns2" > <xsl:output method="xml" indent="yes"/> <xsl:key name="k" match="ns2:UserId" use="text()"/> <xsl:template match="/"> <root> <xsl:apply-templates select="//ns2:Details/ns2:UserId[not(node() = preceding-sibling::node())]"/> </root> </xsl:template> <xsl:template match="//ns2:Details"> <xsl:param name="userId" select="ns2:UserId"/> <ns2:Details> <xsl:copy-of select="key('k', $userId)[not(node() = preceding-sibling::node())]"/> <xsl:copy-of select="./*[name() != 'ns2:UserId']"/> </ns2:Details> </xsl:template> <xsl:template match="ns2:UserId"> <xsl:apply-templates select=".."> <xsl:with-param name="userId" select="."/> </xsl:apply-templates> </xsl:template> </xsl:stylesheet> 

XML Output:

 <?xml version="1.0" encoding="utf-8"?> <root xmlns:ns2="ns2"> <ns2:Details> <ns2:UserId>46</ns2:UserId> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> </ns2:Details> <ns2:Details> <ns2:UserId>237</ns2:UserId> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> </ns2:Details> </root> 
+3
source

This conversion (short, only two patterns, not xsl:for-each , no modes):

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="kIdByVal" match="ns2:UserId" use="."/> <xsl:template match="/"> <xsl:apply-templates select= "ns2:Details/ns2:UserId [generate-id()=generate-id(key('kIdByVal',.)[1])] "/> </xsl:template> <xsl:template match="ns2:UserId"> <ns2:Details> <xsl:copy-of select= "../node() [not(self::ns2:UserId [not(generate-id()=generate-id(current()))]) ]"/> </ns2:Details> </xsl:template> </xsl:stylesheet> 

when applied to this XML document (containing redundant ns2:UserId ):

 <ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> <ns2:UserId>237</ns2:UserId> <ns2:UserId>46</ns2:UserId> </ns2:Details> 

produces exactly the necessary, correct result :

 <ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> </ns2:Details> <ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>237</ns2:UserId> </ns2:Details> 

Explanation : Muenchian grouping, xsl:copy-of , using current()

+1
source

Yes it is possible. You can loop through with for each loop , using for each ns2: UserID node.

0
source

One way to achieve the desired result is to use Identity Transformation and override ns2:Details node.

In the top template, you can use xsl:for-each repeat UserId to repeat all UserId .

To manage the duplicate UserId , you can use the well-known predicate that comes from the Menuchian grouping method.

Since we will use identity transformation, the way to create the whole thing is much easier.

  <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:key name="UserId" match="ns2:UserId" use="."/> <xsl:template match="node()|@*"> <xsl:copy> <xsl:apply-templates select="node()|@*"/> </xsl:copy> </xsl:template> <xsl:template match="ns2:Details"> <xsl:for-each select="ns2:UserId [generate-id() = generate-id(key('UserId',.)[1])]"> <ns2:Details> <xsl:copy-of select="../@*"/> <xsl:apply-templates select="../node() [not(self::ns2:UserId)]"/> <xsl:apply-templates select="."/> </ns2:Details> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

When this transformation is applied to the input entered into the question, the following fragment is obtained:

 <ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> </ns2:Details> <ns2:Details xmlns:ns2="ns"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>237</ns2:UserId> </ns2:Details> 

This conclusion is obtained even if duplicate UserId are present in the input document.

0
source

The following stylesheet handles duplicates:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="ns2"> <xsl:output method="xml" indent="yes" /> <xsl:key name="byUserId" match="ns2:UserId" use="." /> <xsl:template match="/"> <root> <xsl:apply-templates select="ns2:Details/ns2:UserId [generate-id()=generate-id(key('byUserId', .)[1])]" /> </root> </xsl:template> <xsl:template match="ns2:UserId"> <xsl:apply-templates select=".." mode="out"> <xsl:with-param name="userId" select="." /> </xsl:apply-templates> </xsl:template> <xsl:template match="ns2:Details" mode="out"> <xsl:param name="userId" select="''" /> <xsl:copy> <xsl:apply-templates select="node()|@*" mode="out" /> <xsl:copy-of select="$userId"/> </xsl:copy> </xsl:template> <xsl:template match="ns2:UserId" mode="out" /> <xsl:template match="node()|@*" mode="out"> <xsl:copy> <xsl:apply-templates select="node()|@*" mode="out" /> </xsl:copy> </xsl:template> </xsl:stylesheet> 

At this input:

 <ns2:Details xmlns:ns2="ns2"> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> <ns2:UserId>237</ns2:UserId> <ns2:UserId>46</ns2:UserId> </ns2:Details> 

It produces:

 <root xmlns:ns2="ns2"> <ns2:Details> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>46</ns2:UserId> </ns2:Details> <ns2:Details> <ns2:var1>AA0511201143</ns2:var1> <ns2:var2>PARCEL</ns2:var2> <ns2:var3>04/04/2011</ns2:var3> <ns2:var4>Organization</ns2:var4> <ns2:UserId>237</ns2:UserId> </ns2:Details> </root> 
0
source

In addition to being simple in XSLT 2.0. Instead of writing multiple temlates using templates, see code with a single template below, it will give the same result.

  <xsl:for-each-group select="*:Details" group-by="*:UserId"> <xsl:comment select="current-grouping-key()"/> <ns2:Details> <xsl:for-each select="current-group()"> <xsl:element name="ns2:UserId"> <xsl:value-of select="current-grouping-key()"/> </xsl:element> <xsl:copy-of select="*[name() != 'ns2:UserId']"/> </xsl:for-each> </ns2:Details> </xsl:for-each-group> </xsl:template> 
0
source

Source: https://habr.com/ru/post/892574/


All Articles