I launched test from SoapUI and I want to figure out an XQuery assert. I am not able to write the XQuery request.
My xml file (answer request SoapUI)
<parent>
<total1>10.000</total1>
<total2>15</total2>
<value1>1</value1>
<value2>2</value2>
</parent>
Expected answer
I want only nodes starting with total, and the number value of node value
<parent>
<total1>10.0</total1>
<total2>15.0</total2>
</parent>
Current not working XQuery request
<parent>
{ for $n in //parent/*[starts-with(name(),'total')]
return ($n/name() ,$n/number(text()) )
}
</parent>
This is not right because it display only node name. All nodes are on the same line : <parent> total1 10.0 total2 15.0 </parent>
I am OK to do it using XQuery or XPath
New element nodes can be created with element constructors:
<parent>{
for $n in //parent/*[starts-with(name(), 'total')]
return element { name($n) } { number($n) }
}</parent>
The function call number(<total1>10.000</total1>) yields 10 instead of 10.0. If you need a customized representation of your numeric values, the format-number function can be used:
<parent>{
for $n in //parent/*[starts-with(name(), 'total')]
return element { name($n) } { format-number(number($n), '#.0') }
}</parent>
Related
i want to get every <path> where <id_wert> = 1 from a xml file
my query in sql would be "select path from xml where id = 1
<xml_export>
<id>1<\id>
<path>\DATEN\00000001.003</path>
</xml_export>
<xml_export>
<id>2</id>
<path>\DATEN\00000001.004</path>
</xml_export>
<xml_export>
<id>1</id>
<path>\DATEN\00000001.005</path>
</xml_export>
You can locate xml_export parent nodes by child id with desired value and then to get their child path nodes.
As following:
"//xml_export[.//id='1']//path"
In case you want to make it more strict, to search for only cases where id and path are direct children of xml_export the former expression can be changed to be
"//xml_export[./id='1']/path"
Is there an Xpath to find a cousin node that has an element that matches the value of an element in the current node?
Please see below - I am iterating over each "Order" node and want to return the value of LocationID from the Collection node that has the same OrderLoadRef value as the order. For the first order it should return "AAA", for the second it should return "BBB".
The XPath works if I change the value of the OrderLoadRef manually, but how to I set it to be the value of the OrderLoadRef in the current Order Element? I've tried using the self axis, but think by the time we get to the condition, "self" is the collection node, not the order?
I can't hard code relative collection / order node positions as there could be a variable number of these nodes in the XML that my parser receives.
XDocument xDoc = XDocument.Parse(#"<DocRoot>
<Load>
<Collections>
<Collection>
<OrderLoadRef>1</OrderLoadRef>
<LocationID>AAA</LocationID>
</Collection>
<Collection>
<OrderLoadRef>2</OrderLoadRef>
<LocationID>BBB</LocationID>
</Collection>
</Collections>
<Orders>
<Order>
<OrderRef>1521505</OrderRef>
<OrderLoadRef>1</OrderLoadRef>
</Order>
<Order>
<OrderRef>1521505_2</OrderRef>
<OrderLoadRef>2</OrderLoadRef>
</Order>
</Orders>
</Load>
</DocRoot>");
List<XElement> orders = xDoc.XPathSelectElements("//Order").ToList();
foreach(XElement order in orders)
{
string locationId = order.XPathSelectElement("parent::Orders/parent::Load/Collections/Collection[OrderLoadRef = {OrderLoadRef from current order element}]/LocationID").Value;
}
Edited to add: I need this to be a purely XPath solution as I'm not able to alter the C# code in the parser. More than happy to be told it's not possible, but wanted to make sure before I relayed the message!
As Mads said, XPath 3 and later (i.e. the current version 3.1) allows you to use a let expression so e.g.
for $order in /DocRoot/Load/Orders/Order
return
let $col := /DocRoot/Load/Collections/Collection[OrderLoadRef = $order/OrderLoadRef]/LocationID
return $col
is pure XPath 3 and returns (for your sample) the two LocationID elements:
<LocationID>AAA</LocationID>
<LocationID>BBB</LocationID>
In the .NET framework XmlPrime and Saxon.NET support XPath 3.1 and XQuery 3.1 although only XmlPrime has extension methods for C# to work against XDocument, I think, Saxon.NET does allow XPath 3.1 against its XDM tree model or against System.Xml.XmlDocument.
XPath 3.0 (and greater) supports let expressions, which would allow you to do what you want. You could let a variable with the OrderLoadRef from the context node and use it within a predicate selecting the desired Collection by it's OrderLoadRef.
For a static XPath 1.0 expression, I don't think you can achieve what you want. You would need to construct the XPath using the context node information.
Inside your for loop, create a variable for the Order's OrderLoadRef value. Use that value to construct the XPath that you want to evaluate to then select the locationId
foreach(XElement order in orders)
{
string orderLoadRef = order.XPathSelectElement("OrderLoadRef").Value;
string locationId = order.XPathSelectElement("ancestor::Load/Collections/Collection[OrderLoadRef = " + orderLoadRef + "]/LocationID").Value;
//do something with the locationId
}
My project needs to be able to reference a field in a parent group in a repeating section of the child group. Here's the setup. I've labeled each location: AB and C are for my own testing purposes. D is the one that actually needs to print. A is the only one that actually will print. The confusing part is that the 'for-each-check' group loops on each Q_P58W1, of which P58WCHCK is a member, but it only prints outside that grouping.
For the record, that code right after each for-each group is not there in the actual document, I just pasted it there to make it clear how the grouping structure works.
Here is a copy of the xml I'm working with (edited to remove sensitive info):
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated by Oracle BI Publisher 12.2.1.3.0 -Dataengine, datamodel:_ICIM_DM_P58R01_DM_xdm -->
<DATA_DS><P_JNO_IN>271720364</P_JNO_IN>
<Q_A01F>
<A01SEQ>1</A01SEQ><VOID_LINE1>VOID VOID VOID VOID VOID</VOID_LINE1><VOID_LINE2> VOID VOID VOID VOID</VOID_LINE2><LOCATION_IN>0005</LOCATION_IN><AHL1>03</AHL1><ACCOUNT_IN>290</ACCOUNT_IN>
<Q_P58W1>
<P58WCHCK>09077475</P58WCHCK>
<G_1>
<P58WVENNO>93373</P58WVENNO><P58WNAME1>FASTSIGNS</P58WNAME1><P58WADR1></P58WADR1><P58WCSZ></P58WCSZ><P58WPAYN>$12.00</P58WPAYN><P58WPAYW1>Twelve Dollars and 00 Cents*****</P58WPAYW1><P58WGAMT>12</P58WGAMT><P58WDAMT>0</P58WDAMT><P58WNAMT>12</P58WNAMT><P58WCDATE>11 21 19</P58WCDATE><C_PRT_CHK></C_PRT_CHK><PAY_WORDS1>Twelve Dollars and 00 Cents*****</PAY_WORDS1><ADR_LINE1>FASTSIGNS</ADR_LINE1><ADR_LINE3></ADR_LINE3><ADR_LINE6></ADR_LINE6><ACCOUNT_NAME></ACCOUNT_NAME><COUNTRY></COUNTRY><ADDRESS1></ADDRESS1><ADDRESS2></ADDRESS2><ADDRESS3>VOID VOID VOID</ADDRESS3><CITY></CITY><STATE></STATE><ZIP></ZIP><DUMMY_FLAG>DUMMY</DUMMY_FLAG><LOGO_FLAG></LOGO_FLAG><ADR_LINES>FASTSIGNS
</ADR_LINES>
<Q_P58W2>
<P58WINVNO>12</P58WINVNO><P58WVOUCH>637866</P58WVOUCH><P58WIDATE>2019-11-22T00:00:00.000-06:00</P58WIDATE><P58WGAMT>12</P58WGAMT><P58WDAMT>0</P58WDAMT><P58WNAMT>12</P58WNAMT><C_PRT_DATE>11/22/19</C_PRT_DATE>
</Q_P58W2>
<C_PRT_PAY_TOT>$12.00</C_PRT_PAY_TOT>
<CS_NUM_VOUCHERS>1</CS_NUM_VOUCHERS>
</G_1>
<G_1>
<P58WVENNO>93373</P58WVENNO><P58WNAME1>FASTSIGNS</P58WNAME1><P58WADR1></P58WADR1><P58WCSZ></P58WCSZ><P58WPAYN>$12.00</P58WPAYN><P58WPAYW1>Twelve Dollars and 00 Cents*****</P58WPAYW1><P58WGAMT>12</P58WGAMT><P58WDAMT>0</P58WDAMT><P58WNAMT>12</P58WNAMT><P58WCDATE>11 21 19</P58WCDATE><C_PRT_CHK></C_PRT_CHK><PAY_WORDS1>Twelve Dollars and 00 Cents*****</PAY_WORDS1><ADR_LINE1>FASTSIGNS</ADR_LINE1><ADR_LINE3></ADR_LINE3><ADR_LINE6></ADR_LINE6><ACCOUNT_NAME></ACCOUNT_NAME><ACCOUNT_NUMBER></ACCOUNT_NUMBER><ROUTING_NUMBER></ROUTING_NUMBER><COUNTRY></COUNTRY><ADDRESS1></ADDRESS1><ADDRESS2></ADDRESS2><ADDRESS3>VOID VOID VOID</ADDRESS3><CITY></CITY><STATE></STATE><ZIP></ZIP><DUMMY_FLAG>NORMAL</DUMMY_FLAG><LOGO_FLAG></LOGO_FLAG><ADR_LINES>FASTSIGNS
</ADR_LINES>
<Q_P58W2>
<P58WINVNO>12</P58WINVNO><P58WVOUCH>637866</P58WVOUCH><P58WIDATE>2019-11-22T00:00:00.000-06:00</P58WIDATE><P58WGAMT>12</P58WGAMT><P58WDAMT>0</P58WDAMT><P58WNAMT>12</P58WNAMT><C_PRT_DATE>11/22/19</C_PRT_DATE>
</Q_P58W2>
<C_PRT_PAY_TOT>$12.00</C_PRT_PAY_TOT>
<CS_NUM_VOUCHERS>1</CS_NUM_VOUCHERS>
</G_1>
</Q_P58W1>
</Q_A01F>
<CF_PROPERTIES>
<REPORT_NAME>P58R01</REPORT_NAME><REPORT_DESCRIPTION>Check Print And Update</REPORT_DESCRIPTION><VERSION_NUMBER>V11.0.001</VERSION_NUMBER><DATABASE_NAME>DEV</DATABASE_NAME><USER_NAME></USER_NAME><RUN_DATE>2019-11-22T10:39:20.000-06:00</RUN_DATE>
</CF_PROPERTIES>
</DATA_DS>
Hard to tell exactly what you want, but I think I understand. Just use a ../ for each level you want to move up.
XML Example:
<LIST_PARENT>
<PARENT>
<PARENT_NUMBER>1</PARENT_NUMBER>
<PARENT_ATTRIBUTE>A</PARENT_ATTRIBUTE>
<LIST_CHILD>
<CHILD>
<CHILD_NUMBER>1</CHILD_NUMBER>
<CHILD_ATTRIBUTE>A</CHILD_ATTRIBUTE>
</CHILD>
<CHILD>
<CHILD_NUMBER>2</CHILD_NUMBER>
<CHILD_ATTRIBUTE>B</CHILD_ATTRIBUTE>
</CHILD>
</LIST_CHILD>
</PARENT>
<PARENT>
<PARENT_NUMBER>2</PARENT_NUMBER>
<PARENT_ATTRIBUTE>B</PARENT_ATTRIBUTE>
<LIST_CHILD>
<CHILD>
<CHILD_NUMBER>1</CHILD_NUMBER>
<CHILD_ATTRIBUTE>A</CHILD_ATTRIBUTE>
</CHILD>
<CHILD>
<CHILD_NUMBER>2</CHILD_NUMBER>
<CHILD_ATTRIBUTE>B</CHILD_ATTRIBUTE>
</CHILD>
</LIST_CHILD>
</PARENT>
</LIST_PARENT>
RTF Code:
<for-each:PARENT>
Parent Number: <PARENT_NUMBER>
Parent Attribute: <PARENT_ATTRIBUTE>
<for-each:CHILD>
Child Number: <CHILD_NUMBER>
Child Attribute: <CHILD_ATTRIBUTE>
Parent Number: <../../PARENT_ATTRIBUTE>
<end for-each>
<end for-each>
The following works:
<?for-each:Q_P58W1?>
<?P58WCHCK?>
<?end for-each?>
When you add the repeating group for-each-check, you are already looping over Q_P58W1. The FOProcessor just interprets this as <?for-each:Q_P58W1?>. Then you added another <?for-each:Q_P58W1?> in text, but the FOProcessor interprets this the same way.
This makes your example code:
<?P58WCHCK?>
<?for-each:Q_P58W1?><?for-each:Q_P58W1?>
<?P58WCHCK?>
...
<?end for-each?>
In your example it is unclear how you close the for-each loop. If you don't close the for-each loop both twice, your syntax is incorrect. If you do close it, you are trying to print Q_P58W1/Q_P58W1/P58WCHCK, which does not exist.
SOLUTION:
Remove the textual <?for-each:Q_P58W1?> and <?for-each:G_1?>, and if applicable the matching closing <?end for-each?>.
I found an answer to this after a bit more looking around. The element I wanted to include in the child group - Q_P58W1/P58WCHCK - refused to print inside the <?for-each:G_1?> loop, so I just changed my BI Publisher data model so that the query selected the check number twice, as two different elements with different aliases. One of those, P58WCHCK, remained as the single element grouped under Q_P58W1 since that was a required functionality. The other just became a part of each row of the child group. Here's a screenshot of the data model box for clarity.
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.
Anyone know how to get the position of a node using XPath?
Say I have the following xml:
<a>
<b>zyx</b>
<b>wvu</b>
<b>tsr</b>
<b>qpo</b>
</a>
I can use the following xpath query to select the third <b> node (<b>tsr</b>):
a/b[.='tsr']
Which is all well and good but I want to return the ordinal position of that node, something like:
a/b[.='tsr']/position()
(but a bit more working!)
Is it even possible?
edit: Forgot to mention am using .net 2 so it's xpath 1.0!
Update: Ended up using James Sulak's excellent answer. For those that are interested here's my implementation in C#:
int position = doc.SelectNodes("a/b[.='tsr']/preceding-sibling::b").Count + 1;
// Check the node actually exists
if (position > 1 || doc.SelectSingleNode("a/b[.='tsr']") != null)
{
Console.WriteLine("Found at position = {0}", position);
}
Try:
count(a/b[.='tsr']/preceding-sibling::*)+1.
You can do this with XSLT but I'm not sure about straight XPath.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"
omit-xml-declaration="yes"/>
<xsl:template match="a/*[text()='tsr']">
<xsl:number value-of="position()"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
I realize that the post is ancient.. but..
replace'ing the asterisk with the nodename would give you better results
count(a/b[.='tsr']/preceding::a)+1.
instead of
count(a/b[.='tsr']/preceding::*)+1.
If you ever upgrade to XPath 2.0, note that it provides function index-of, it solves problem this way:
index-of(//b, //b[.='tsr'])
Where:
1st parameter is sequence for searching
2nd is what to search
Unlike stated previously 'preceding-sibling' is really the axis to use, not 'preceding' which does something completely different, it selects everything in the document that is before the start tag of the current node. (see http://www.w3schools.com/xpath/xpath_axes.asp)
Just a note to the answer done by James Sulak.
If you want to take into consideration that the node may not exist and want to keep it purely XPATH, then try the following that will return 0 if the node does not exist.
count(a/b[.='tsr']/preceding-sibling::*)+number(boolean(a/b[.='tsr']))
The problem is that the position of the node doesn't mean much without a context.
The following code will give you the location of the node in its parent child nodes
using System;
using System.Xml;
public class XpathFinder
{
public static void Main(string[] args)
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(args[0]);
foreach ( XmlNode xn in xmldoc.SelectNodes(args[1]) )
{
for (int i = 0; i < xn.ParentNode.ChildNodes.Count; i++)
{
if ( xn.ParentNode.ChildNodes[i].Equals( xn ) )
{
Console.Out.WriteLine( i );
break;
}
}
}
}
}
I do a lot of Novell Identity Manager stuff, and XPATH in that context looks a little different.
Assume the value you are looking for is in a string variable, called TARGET, then the XPATH would be:
count(attr/value[.='$TARGET']/preceding-sibling::*)+1
Additionally it was pointed out that to save a few characters of space, the following would work as well:
count(attr/value[.='$TARGET']/preceding::*) + 1
I also posted a prettier version of this at Novell's Cool Solutions: Using XPATH to get the position node