I need with xmlstarlet or yq to copy the content of an element into an other element, placing to the start or the end of it.
Using this kind of xml:
<products>
<product>
<id>01</id>
<Title><![CDATA[ Product 1 Title ]]></Title>
<Dimensions><![CDATA[ S ]]></Dimensions>
<Size><![CDATA[ for Adult ]]></Size>
</product>
<product>
<id>02</id>
<Title><![CDATA[ Product 2 Title ]]></Title>
<Dimensions><![CDATA[ Medium ]]></Dimensions>
<Size><![CDATA[ for Kids ]]></Size>
</product>
</products>
i try to copy the content of each Dimensions and Size elements into the start or end of Title element, using this bash:
xmlstarlet ed -u /products/product/Title -x "concat(/products/product/Title,' ',/products/product/Dimensions/text(),' ',/products/product/Size)" sourcefile.xml > outputfile.xml
but the problem is that the Title of first product element is copied in every other product element. I expect:
<Title><![CDATA[ Product 1 Title S for Adult]]></Title>
<Title><![CDATA[ Product 2 Title Medium for Kids]]></Title>
but i receive:
<Title><![CDATA[ Product 1 Title S for Adult]]></Title>
<Title><![CDATA[ Product 1 Title S for Adult]]></Title>
CodePudding user response:
Which implementation of yq do you mean?
Using mikefarah/yq:
yq provides the options -p and -o to set the input and output format; both take the parameter xml or x.
yq -o xml -p xml '
.products.product[] |= (.Title = " " .Dimensions " " .Size)
' sourcefile.xml > outputfile.xml
Using kislyuk/yq:
To manipulate XML files, yq provides xq, which converts the XML file into JSON, then uses stedolan/jq for manipulation. The -x option converts the output back to XML:
xq -x '
.products.product[] |= (.Title = " " .Dimensions " " .Size)
' sourcefile.xml > outputfile.xml
Output
For both, the outputfile.xml should then look like:
<products>
<product>
<id>01</id>
<Title>Product 1 Title S for Adult</Title>
<Dimensions>S</Dimensions>
<Size>for Adult</Size>
</product>
<product>
<id>02</id>
<Title>Product 2 Title Medium for Kids</Title>
<Dimensions>Medium</Dimensions>
<Size>for Kids</Size>
</product>
</products>
Using xmlstarlet
Here you can go with:
xmlstarlet ed -u /products/product/Title -x '
concat(../Title, ../Dimensions, ../Size)
' sourcefile.xml > outputfile.xml
Then, the outputfile.xml would then look like:
<?xml version="1.0"?>
<products>
<product>
<id>01</id>
<Title> Product 1 Title S for Adult </Title>
<Dimensions><![CDATA[ S ]]></Dimensions>
<Size><![CDATA[ for Adult ]]></Size>
</product>
<product>
<id>02</id>
<Title> Product 2 Title Medium for Kids </Title>
<Dimensions><![CDATA[ Medium ]]></Dimensions>
<Size><![CDATA[ for Kids ]]></Size>
</product>
</products>
Note: The important thing (for all three approaches) is to traverse to each product, and collect the items for concatenation from their children only. With both yq implementations, this is achieved using the update operator |=. For xmlstarlet you need to move with relative XPaths using .. to go up one level.
Also note, that there are formatting/encoding differences between these tools. Use them according to your needs.
