Parsing XML with REXML - ruby

I have this XML document and I want to find an specific GitHubCommiter using REXML. Hoy do I do that?
<users>
<GitHubCommiter id="Nerian">
<username>name</username>
<password>12345</password>
</GitHubCommiter>
<GitHubCommiter id="xmawet">
<username>name</username>
<password>12345</password>
</GitHubCommiter>
<GitHubCommiter id="JulienChristophe">
<username>name</username>
<password>12345</password>
</GitHubCommiter>
</users>
I have tried:
log = REXML::Document.new(file)
root = log.root username = root.elements["GitHubCommiter['#{github_user_name}']"].elements['username'].text
password = root.elements["GitHubCommiter['#{github_user_name}']"].elements['password'].text
root.elements["GitHubCommiter['id'=>'#{github_user_name}']"].text
But I don't find a way to do it. Any idea?

The docs say for elements (emphasis mine):
[]( index, name=nil)
Fetches a child element. Filters only Element children, regardless of the XPath match.
index: the search parameter. This is either an Integer, which will be used to find the index‘th child Element, or an XPath, which will be used to search for the Element.
So it needs to be XPath:
root.elements["./GitHubCommiter[#id = '{github_user_name}']"]
etc.

Related

XPath Syntax - XSL 1.0

I'm trying to select all elements using XSL and XPath syntax where there is more than one pickup. I'd like to return the counter_name for each. Can someone please help me with the syntax? In this example there is only one counter_name with pickup locations, but there could be multiple locations where there are pickup counters.
XPATH
<xsl:value-of select="results/unique_locations/partner_location_ids[count(pickup) > 0]/counter_name" /><br/>
XML
<results>
<unique_locations>
<counter_name>Lake Buena Vista, FL</counter_name>
<is_airport>N</is_airport>
<partner_location_ids>
<pickup>
</pickup>
<dropoff>
<container>ZR-ORLS001</container>
<container>ET-ORLR062</container>
<container>HZ-ORLS011</container>
<container>HZ-ORLW015</container>
<container>AV-ORLR004</container>
</dropoff>
</partner_location_ids>
<counter_name>Orlando, FL</counter_name>
<is_airport>N</is_airport>
<partner_location_ids>
<pickup>
<container>ET-ORLC037</container>
<container>AV-ORLC021</container>
<container>ET-ORLC033</container>
<container>ET-ORLC035</container>
<container>HZ-ORLS007</container>
<container>HZ-ORLC004</container>
<container>HZ-ORLC002</container>
<container>ZR-ORLS002</container>
<container>BU-ORLE002</container>
<container>AV-ORLC019</container>
<container>ET-ORLR064</container>
<container>ET-ORLC001</container>
<container>ET-ORLR063</container>
<container>ET-ORLR061</container>
<container>HZ-ORLC011</container>
<container>HZ-ORLC054</container>
<container>HZ-ORLN003</container>
<container>HZ-ORLC007</container>
<container>HZ-ORLC005</container>
<container>ZA-ORLC002</container>
<container>ZA-ORLC003</container>
<container>ZA-ORLC001</container>
<container>AV-ORLC002</container>
<container>AV-ORLC001</container>
<container>BU-ORLS001</container>
<container>ET-ORLC012</container>
<container>AL-ORLR071</container>
<container>HZ-ORLC022</container>
<container>ET-ORLC051</container>
<container>HZ-ORLC025</container>
<container>HZ-ORLN018</container>
<container>HZ-ORLC017</container>
<container>AV-ORLN003</container>
<container>BU-ORLC002</container>
<container>BU-ORLC003</container>
<container>BU-ORLS006</container>
<container>ET-ORLC027</container>
<container>ET-ORLC022</container>
<container>AL-ORLR081</container>
<container>BU-ORLC005</container>
<container>HZ-ORLR029</container>
<container>HZ-ORLC032</container>
<container>HZ-ORLC031</container>
<container>HZ-ORLC030</container>
<container>ET-ORLC021</container>
</pickup>
<dropoff>
<container>HZ-ORLC003</container>
<container>ZA-ORLC004</container>
<container>BU-ORLW002</container>
<container>HZ-ORLC026</container>
<container>ZR-ORLC010</container>
<container>AL-ORLR073</container>
</dropoff>
</partner_location_ids>
</unique_locations>
Your XML structure is non-ideal, in that it appears to contain elements that are associated with each other by sequence, rather than exclusively by containment within the same element. But XPath can deal with that.
Supposing that the context node for evaluation of the XPath is the parent node of the <results> whose contents you are examining, it appears you want something along these lines:
results/unique_locations/partner_location_ids[pickup/*]/preceding-sibling::counter_name
Note in the first place the predicate: [pickup/*]. The expression within, interpreted in boolean context, evaluates to true if the expression matches any nodes. That's why we need pickup/*, not just pickup, to distinguish between <pickup> elements that contain child nodes and those that don't.
Additionally, observe the use of the preceding-sibling axis instead of the default child axis to step from each matching <partner_location_ids> to its corresponding (I think) <counter_name>.

xpath expression to read value based on value of sibling

I've below xml and would like to read the value of 'Value' tag whose Name matches 'test2'. I'm using the below xpath , but did not work. Can someone help.
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']/*[ local-name()='Name'][normalize-space(.) = 'test2']//*[local-name()='Value']/text()
<get:OutputData>
<get:OutputDataItem>
<get:Name>test1</get:Name>
<get:Value/>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>test2</get:Name>
<get:Value>B5B4</get:Value>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>test3</get:Name>
<get:Value/>
</get:OutputDataItem>
<get:OutputDataItem>
<get:Name>OP_VCscEncrptCd_VAR</get:Name>
<get:Value/>
</get:OutputDataItem>
</get:OutputData>
Thanks
You were close, but because the get:name and get:value are siblings, you need to adjust your XPath a little.
Your XPath was attempting to address get:value elements that were descendants of get:name, rather than as siblings. Move the criteria that is filtering the get:name into a predicate, then step down into the get:value:
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']
[*[ local-name()='Name'][normalize-space(.) = 'test2']]/*[local-name()='Value']/text()
You could also combine the criteria of the predicate filter on the get:name and use an and:
/*[ local-name()='OutputData']/*[ local-name()='OutputDataItem']
[*[ local-name()='Name' and normalize-space(.) = 'test2']]/*[local-name()='Value']/text()
This should work I think:
//*[local-name()="get:Name" and text()="test2"]/following-sibling::*[local-name()="get:Value"]/text()

Xpath get multiple node-names

i am using xpath to get some node names from a xhtml / xml file.
I currently have this xpath:
/xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')]
That will get the nodes with a name like this:
section-1_s1_partners
section-2-s2_strategy
The result of the above xpath are the matched nodes, but i want to get for each match the full-node-name. When i use the name() function like
name(/xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')])
Then it only returns the first match, and i have no clue how to do it otherwise..
Any great ideas??
Thanks!
(the xhtml/xml: )
<xhtml:html ....>
<xhtml:head>
<xhtml:title>ASD-1</xhtml:title>
<xforms:model id="fr-form-model">
<xforms:instance id="fr-form-instance">
<form>
<section-1_s1_partners>
<control-304/>
<toggleForm>ASD</toggleForm>
<applicationid/>
<section-345>
<s1_kbPaAAr/>
<s1_kbDCCent/>
<s1_kbRAE/>
</section-345>
<section-s1_depDDFentGFress>
<address_search/>
<address_postcode/>
<address_address1/>
<address_address2/>
<address_address3/>
<address_city/>
</section-s1_departmentAddress>
<section-344>
<s1_companyPartner/>
<s1_companyRegistrationNumber/>
<s1_companyType/>
<s1_companySize/>
</section-344>
<section-s1_companyAddress>
<address_search/>
<address_postcode/>
<address_address1/>
<address_address2/>
<address_address3/>
<address_city/>
</section-s1_companyAddress>
<section-324>
<s1_plannedDate/>
<s1_workDescription/>
<s1_publicDescription/>
<s1_numberOfAssociates>1</s1_numberOfAssociates>
<s1_duration/>
<s1b_resubmissionYesNo/>
<s1_GAAGrogramNumber/>
</section-324>
</section-1_s1_partners>
<section-2-s2_strategy>
<control-4/>
<s2_memo_strategic/>
<s2_memo_problems/>
<s2_companyPosition/>
<s2_companyContribution/>
<s2_lackExpertise/>
<s2_essential/>
<s2_companySponsor/>
<s2_seekKnowledge/>
<s2_challenge/>
</section-2-s2_strategy>
The name function need one argument, it cannot take a node list. You have to iterate over the nodelist in the language you are using. For example, in xsh:
for /xhtml:html/xhtml:head/xforms:model/xforms:instance/form/*[starts-with(local-name(), 'section')]
echo name()

Inserting a child node when list is empty (XForms)

My problem is the following :
I usually have those data:
<structures>
<structure id="10">
<code>XXX</code>
</structure>
</structures>
so the table I display (single columns : code) is ok.
But in some cases, the data is the result a a query with no content, so the data is:
<structures/>
resulting in my table not displaying + error.
I am trying to insert, in the case of an empty instance, a single node so that the data would look like:
<structures>
<structure id="0"/>
</structures>
I am trying something like that :
<xforms:action ev:event="xforms-submit-done">
<xforms:insert if="0 = count(instance('{./instance-name}')/root/node())" context="instance('{./instance-name}')/root/node()" origin="xforms:element('structure', '')" />
</xforms:action>
but no node inserted when I look at the data in the inspector in the page.
Any obvious thing I am doing wrong?
There seems to be erros in your XPath if and context expressions:
if="0 = count(instance('{./instance-name}')/root/node())"
context="instance('{./instance-name}')/root/node()"
You are a using curly brackets { and }, I assume to have the behavior of attribute value templates (AVTs). But the if and context expressions are already XPath expressions, so you cannot use AVTs in them. Try instead:
if="0 = count(instance(instance-name)/root/node())"
context="instance(instance-name)/root/node()"
Also, the instance-name path is relative to something which might not be clear when reading or writing the expression. I would suggest using an absolute path for example instance('foo')/instance-name to make things clearer.
You don't provide the structure of the other instances, so I can tell for sure, but you'll expression above suppose that they have the form:
<xf:instance id="foo">
<some-root-element>
<root>
<structure/>
</root>
<some-root-element>
</xf:instance>
I don't know if that's what you intend.
Finally, you could replace count(something) = 0, with empty(something).

JDOM-XPath: Can't get the second value in a collection

I'm pretty confused about this one. Given the following xml:
<sch:eventList>
<sch:event>
<sch:eventName>Event One</sch:eventName>
<sch:locationName>Location One</sch:locationName>
</sch:event>
<sch:event>
<sch:eventName>Event Two</sch:eventName>
<sch:locationName>Location Two</sch:locationName>
</sch:event>
</sch:eventList>
When using JDOM using the following code:
XPath eventNameExpression = XPath.newInstance("//sch:eventName");
XPath eventLocationExpression = XPath.newInstance("//sch:eventLocation");
XPath eventExpression = XPath.newInstance("//sch:event");
List<Element> elements = eventExpression.selectNodes(requestElement);
for(Element e: elements) {
System.out.println(eventNameExpression.valueOf(e));
System.out.println(eventLocationExpression.valueOf(e));
}
The console shows this:
Event One
Location One
Event One
Location One
What am I missing?
Don't use '//' it starts always searching at the root node. Use e.g. './sch:eventName' it is relative to the current node.

Resources