XPath results based on two nodes - ruby

I have XML that has a lot of duplicated values. I'd like to select all the rows with a specific section ("sec") and section tag ("sec_tag"), but I can't seem to get the XPath correct.
Here's a small snippet of the XML:
<root>
<record>
<sec>5</sec>
<sec_tag>919</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
<record>
<sec>5</sec>
<sec_tag>930</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
<record>
<sec>7</sec>
<sec_tag>919</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
</root>
I want the node that has <sec>5</sec> and <sec_tag>919</sec_tag>.
I tried something like this:
//sec[text(), "5"] and //sec_tag[text(), "919"]
Obviously that's not the correct syntax there, I just need to find the correct XPath expression.

You can use the following XPath expression to return record elements having child sec equals 5 and sec_tag equals 919 :
//record[sec = 5 and sec_tag = 919]

Related

XSLT first occurence of element based on duplicate value

The given XML:
<records>
<record>
<country>Germany</country>
<value>123</value>
</record>
<record>
<country>Germany</country>
<value>62</value>
</record>
<record>
<country>Germany</country>
<value>033</value>
</record>
<record>
<country>Armenia</country>
<value>444</value>
</record>
<record>
<country>Armenia</country>
<value>212</value>
</record>
<record>
<country>Armenia</country>
<value>864</value>
</record>
</records>
How do I get an output, which respectively chooses every first occurrence of <record> by the value of <country>.
So the desired output should look like:
<records>
<record>
<country>Germany</country>
<value>123</value>
</record>
<record>
<country>Armenia</country>
<value>444</value>
</record>
</records>
UPDATE: Solved my Problem with given XSL
<xsl:key name="country" match="record" use="value" />
<xsl:template match="records">
<xsl:apply-templates select="record[1]" />
</xsl:template>
<xsl:template match="record">
<xsl:for-each select="key('country', value)">
<country><xsl:value-of select="country"/></country>
<value><xsl:value-of select="value"/></value>
</xsl:for-each>
</xsl:template>

Xpath function to loop through repeating nodes

What XPath function works to loop through repeating XML nodes.
This is my Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<Record>
<Type>V</Type>
<Address>
<Qual>A</Qual>
<ID>A1</ID>
</Address>
<Address>
<Qual>A</Qual>
<ID>B2</ID>
</Address>
<Address>
<Qual>C</Qual>
<ID>C2</ID>
</Address>
<Category>
<EL>PO</EL>
</Category>
<Category>
<EL>DP</EL>
</Category>
</Record>
I don't want to process the data if Qualf=A & ID = B2, Category =DP & Type =V
My Xpath does not work due to repeating nodes..
(concat(Xpath./Type,Xpath./Record/Address/Qual,Xpath./Record/Address/ID,Xpath./Record/Category/EL) != "VAB2DP"
so I tried
choose((concat(Xpath./Type,Xpath./Record/Address/Qual,Xpath./Record/Address/ID,Xpath./Record/Category/EL) != "VAB2DP"),'true','false'
It still does not work.

Xpath attribute and text

I am learning for an exam and i can't quite figure out what i am doing wrong here.
i got this xml
<?xml version="1.0"?>
<schema xmlns=""
xmlns:xsi="link-2"
xsi:schemeLocation="link-3">
<wm-stats>
<wm jahr="2014">
<teilnehmer platz="1">Deutschland</teilnehmer>
<teilnehmer platz="2">Argentinien</teilnehmer>
<teilnehmer platz="3">Niederlande</teilnehmer>
</wm>
<wm jahr="2010">
<teilnehmer platz="1">Spanien</teilnehmer>
<teilnehmer platz="2">Holland</teilnehmer>
<teilnehmer platz="3">Deutschland</teilnehmer>
</wm>
<wm jahr="2006">
<teilnehmer platz="1">Italien</teilnehmer>
<teilnehmer platz="2">Frankreich</teilnehmer>
<teilnehmer platz="3">Deutschland</teilnehmer>
</wm>
<record name="Rekordtorschütze">
<person> Miroslav Klose </person> hat in Brasilien ...
</record>
<record name="Rekordweltmeisterschaften">
<ort> Brasilien </ort> ist mit 5 Weltmeistersiegen ...
</record>
</wm-stats>
</schema>
i now need to find all the years where holland was taking part in the championship, i know that i have to look for something like this //wm[#jahr]/teilnehmer[text()="Holland"]
But how do i get the value of jahr now? the correct node to be located would be jahr 2010.
The other way around
//wm[teilnehmer = "Holland"]/#jahr
but your approach is not unsalvageable, either
//wm[#jahr]/teilnehmer[. ="Holland"]/../#jahr
* note that [#jahr] is actually superfluous in this expression
You can always navigate upwards (and sideways) in XPath.
Have a look at this comprehensive image explaining the various XPath axes available for navigation: https://our.umbraco.org/wiki/reference/xslt/xpath-axes-and-their-shortcuts/

Replacing xml tags in BASH

I have a large collection of xml documents with a wide array of different tags in them. I need to change all tags of the form <foo> and turn them into tags of the form <field name="foo"> in a way that will also ignore the attributes of a given tag. That is, a tag of the form <foo id="bar"> should also be changed to the tag <field name="foo">.
In order for this transformation to work, I also need to distinguish between <foo> and </foo>, as </foo> must go to </field>.
I have played around with sed in a bash script, but to no avail.
Although sed is not ideal for this task (see comments; further reading: regular, context-free grammar and xml), it can be pressed into service. Try this one-liner:
sed -e 's/<\([^>\/\ ]*\)[^>]*>/<field name=\"\1\">/g' -e 's/<field name=\"\">/<\/field>/g' file
First it will replace all end tags with </field>, then replace every open tag first words with <field name="firstStoredWord">
This solution prints everything on the standard output. If you want to replace it in file directly when processing, try
sed -i -e 's/<\([^>\/\ ]*\)[^>]*>/<field name=\"\1\">/g' -e 's/<field name=\"\">/<\/field>/g' file
That makes from
<html>
<person>
but <person name="bob"> and <person name="tom"> would both become
</person>
this
<field name="html">
<field name="person">
but <field name="person"> and <field name="person"> would both become
</field>
Sed is the wrong tool for the job - a simple XSL Transform can do this much more reliably:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="foo">
<field name="foo">
<xsl:apply-templates/>
</field>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note that unlike sed, it can handle short empty elements, newlines within tags (e.g. as produced by some tools), and just about anything that's well-formed XML. Here's my test file:
<?xml version="1.0"?>
<doc>
<section>
<foo>Plain foo, simple content</foo>
</section>
<foo attr="0">Foo with attr, with content
<bar/>
<foo attr="shorttag"/>
</foo>
<foo
attr="1"
>multiline</foo
>
<![CDATA[We mustn't transform <foo> in here!]]>
</doc>
which is transformed by the above (using xsltproc 16970175.xslt 16970175.xml) to:
<?xml version="1.0"?>
<doc>
<section>
<field name="foo">Plain foo, simple content</field>
</section>
<field name="foo">Foo with attr, with content
<bar/>
<field name="foo"/>
</field>
<field name="foo">multiline</field>
We mustn't transform <foo> in here!
</doc>

How to reference an XML attribute using XPath?

My XML:
<root>
<cars>
<makes>
<honda year="1995">
<model />
<!-- ... -->
</honda>
<honda year="2000">
<!-- ... -->
</honda>
</makes>
</cars>
</root>
I need a XPath that will get me all models for <honda> with year 1995.
so:
/root/cars/makes/honda
But how to reference an attribute?
"I need a XPath that will get me all models for <honda> with year 1995."
That would be:
/root/cars/makes/honda[#year = '1995']/model
Try /root/cars/makes/honda/#year
UPDATE: reading your question again:
/root/cars/makes/honda[#year = '1995']
Bottom line is: use # character to reference xml attributes.

Resources