Home > Mobile >  XMLstarlet copy element's content between elements
XMLstarlet copy element's content between elements

Time:11-27

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.

  • Related