Search code examples
xmlstarlet

Repeat attribute with xmlstarlet


I'm trying to use XMLstarlet to convert some data from XML format to CSV. My data is formatted as

<!-- mydata.xml -->
<alldata>
    <data id="first">
        <coord><x>0</x><y>5</y></coord>
        <coord><x>1</x><y>4</y></coord>
        <coord><x>2</x><y>3</y></coord>
    </data>
    <data id="second">
        <coord><x>3</x><y>2</y></coord>
        <coord><x>4</x><y>1</y></coord>
        <coord><x>5</x><y>0</y></coord>
    </data>
</alldata>

I would like to format this data in three columns "id", "x", and "y" to get:

first;0;5
first;1;4
first;2;3
second;3;2
second;4;1
second;5;0

My attempts with XMLstarlet has failed indicating that I don't really understand what I am doing.

xml sel -T -t -m /alldata/data -v "@id" -m /alldata/data/coord -v "concat(x,';',y)" -n mydata.xml

gives me:

first0;5
1;4
2;3
3;2
4;1
5;0
second0;5
1;4
2;3
3;2
4;1
5;0

which is not what I want or expected. Is it possible to modify my query to get the desired output?


Solution

  • -m /alldata/data -v "@id" -m /alldata/data/coord -v "concat(x,';',y)"
    

    The main problem is the -m /alldata/data/coord: this matches all the coord elements in the entire document, what you really wanted was just the coord under the current data elements:

    -m /alldata/data -v "@id" -m coord -v "concat(x,';',y)"
    

    You also want the id on every line so you need to move into the inner loop:

    -m /alldata/data -m coord -v "concat(../@id,';',x,';',y)"
    

    At this point there is no benefit to having 2 nested loops so we can simplify:

    xml sel -T -t -m /alldata/data/coord -v "concat(../@id,';',x,';',y)" -n mydata.xml