Reputation: 291
I have an XML file which I need to modify. First I need to do a group by and then exclude a couple of nodes.
<cars>
<car>
<make>Toyota</make>
<model>Camry</model>
<color>White</color>
<price>123</price>
</car>
<car>
<make>Honda</make>
<model>Accord</model>
<color>White</color>
<price>423</price>
</car>
</cars>
Here is my code that does the transformation:
<root>
{
for $color in distinct-values(doc('cars.xml')//cars/car/color)
let $car := doc('cars.xml')//cars/car
return <appearance color="{$color}">
{ $car[color eq $color ] }
</appearance>
}
</root>
I get:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<appearance color="White">
<car>
<make>Toyota</make>
<model>Camry</model>
<color>White</color>
<price>123</price>
</car>
<car>
<make>Honda</make>
<model>Accord</model>
<color>White</color>
<price>423</price>
</car>
</appearance>
</root>
This does 95% of what I need except for one problem. I need to exclude nodes "color" and "price" from the final XML while preserving the grouping.
I tried to do the following in my return statement: { $car[color eq $color ]/*[not(self::price)][not(self::color)] }
sort of works but it completely eliminates the "car" element!! Any advice?
Upvotes: 4
Views: 4156
Reputation: 5294
I like black sowl's solution, but I'd suggest the following slightly cleaner version of the code. Cleaner in these ways:
Use singular & plural in variable names to indicate single item vs. sequence
<root>
{
let $cars := doc('cars.xml')//car
let $colors := distinct-values($cars/color)
for $color in $colors
return
<appearance color="{$color}">
{
for $car in $cars[color = $color]
return
<car>
{$car/*[not(self::color or self::price)]}
</car>
}
</appearance>
}
</root>
Upvotes: 1
Reputation: 163595
Note that in XPath, you can't create new nodes. You can either return nodes that already exist in the input document unchanged, or you can compute values such as counts and totals, but if you want a new or modified node tree, you need XSLT or XQuery.
If your output is going to be similar to your input with small changes, then the XSLT solution is often a lot simpler than the XQuery solution.
Upvotes: 0
Reputation: 195
Try this:
<root>
{
let $cars-doc := doc('cars.xml')
let $color := distinct-values($cars-doc//cars/car/color)
return
for $carcolor in $color
return
<appearance color="{$carcolor}">
{
for $car in $cars-doc//cars/car
where $car/color = $carcolor
return
<car>
{$car/*[not(self::color or self::price)]}
</car>
}
</appearance>
}
</root>
Hope this helps.
Upvotes: 7