I have an xml of the following structure:
<Root>
<Sample>
<Materil material_class="book" />
<Book Name="harry" Price="8" />
<Book Name="small things" Price="9" />
<Book Name="snow" Price="10" />
</Sample>
<Commodity>
<Sample>
<Materil material_class="sub" />
<Book Name="sherin" Price="8" />
<Book Name="bigthings" Price="9" />
<Book Name="leopard" Price="10" />
</Sample>
<Commodity>
<Sample>
<Materil material_class="sub" />
<Book Name="azxcv" Price="86" />
<Book Name="ddddd" Price="79" />
<Book Name="qwert" Price="810" />
</Sample>
</Commodity>
<Commodity>
<Sample>
<Materil material_class="subtwo" />
<Book Name="ratnam" Price="86" />
<Book Name="shantharam" Price="99" />
<Book Name="da vinci" Price="10" />
</Sample>
</Commodity>
</Commodity>
</Root>
Is there a way to iterate this xml based on condition like,if the material_class = "sub", iterate the Book tag below that and store the #Name and #Price . If the material_class = "book", iterate the Book tag below that. Also i want to get the length of number of /Root/Commodity/Commodity tags (in this case , it is two). Any help is appreciated. I am new to XPath.
To get the Book #Name and #Price for #material_class='sub' or #material_class='book', use this XPATH
/Root[descendant-or-self::Sample or Sample[descendant-or-self::*]]//*[Materil[#material_class='sub' or #material_class='book']]/Book
OR
//Sample[Materil[#material_class='book' or #material_class='sub']]/Book
After loading this XPATH, to print the Name use #Name and Price use #Price
To get the length of number of /Root/Commodity/Commodity tags, XPATH is
/Root/Commodity/Commodity
OR
//Commodity/Commodity
JAVA:
NodeList node = (NodeList) xpath.evaluate("/Root/Commodity/Commodity", xml, XPathConstants.NODESET);
count = node.getLength(); // OUTPUTS: 2
For your information, actually rendering XML through XSLT is much better performance and easier to implement.
<xsl:apply-templates select="//Sample[Materil[#material_class='sub']]/Book"/>
<xsl:apply-templates select="//Sample[Materil[#material_class='book']]/Book"/>
I do not have access to an XSL editor right now, so have not tested the above, but it will give you an idea. Select the Book, and constrain the Materil with the condition you want.
Related
I need to capture an XML message that is sent as part of a request from SOAPui to Wiremock Server. Upon capturing this file, I would then need to compare it against an XSD file. The hard bit is capturing the XML file, I'm not sure what methods wiremock server has that would enable this.
For example, this is the XML message I want to capture in my java application:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<author>Vaidyanathan Nagarajan</author>
<year>2003</year>
<price>49.99</price>
</book>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
What would I need to capture this?
Here is my XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:base="XYZ" xmlns:d="http://schemas/services" xmlns:m="http://schemas/metadata" xmlns="http://www.abc.pqr/Atom">
<title type="text">Title</title>
<id>Id</id>
<updated>2018-02-08</updated>
<link rel="self" title="titl" href="title" />
<entry m:etag="W/"25"">
<id>id1</id>
<title type="text">title_text</title>
<updated>2018-02-08</updated>
<author>
<name />
</author>
<link m:etag=""{acx},25"" rel="edit-media" title="title_value" />
<link rel="edit" title="title" />
<link rel="http://az.com/CreatedBy" type="application/atom+xml;type=entry" title="CreatedBy" href="title/CreatedBy" />
<link rel="http://az.com/ModifiedBy" type="application/atom+xml;type=entry" title="ModifiedBy" href="title/ModifiedBy" />
<link rel="http://az.com/CheckedOutTo" type="application/atom+xml;type=entry" title="CheckedOutTo" href="title/CheckedOutTo" />
<category term="xyz" scheme="xyz" />
<content type="application/octetstream" src="http://collb.xlsm" />
<m:properties xmlns:m="http://xyz/metadata" xmlns:d="wsx/dataservices">
<d:ContentTypeID>contentId</d:ContentTypeID>
<d:Dept>21</d:Dept>
<d:Class>0</d:Class>
<d:Id m:type="Edm.Int32">2121</d:Id>
</m:properties>
</entry>
<link rel="next" href="http://tyu.com" />
</feed>
I want to read values corresponding to <d:Id m:type> and <entry m:etag> i.e. output should be 2121 and 25.
How should I read these.
P.S: I have gone through similar questions asked on stackoverflow, but nothing worked for me.
Part of my code:
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilder = builderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse(connection.getInputStream());
Element element = doc.getDocumentElement();
element.getAttributeNodeNS("http://schemas/metadata", "type");
You haven't said which of the vast choice of Java APIs you are using. I guess that probably means you're using DOM. I don't know why anyone still uses DOM when there are much better alternatives available, but they do.
Whichever API you are using, you'll find that there's probably a method something like
element.getAttributeValue(namespace, localname)
To read the m:type attribute, you would use
element.getAttributeValue("http://schemas/metadata", "type")
Below XML is provided for example.
<?xml version="1.0" encoding="utf-8"?>
<store d:mi="22">
<book price="12.99" d:price="Number" d:mi="4">
<title d:constr="String" d:mi="1">Sword of Honour</title>
<category d:constr="String" d:mi="2">fiction</category>
<author d:constr="String" d:mi="3">Evelyn Waugh</author>
</book>
<book price="8.99" d:price="Number" d:mi="9">
<sublist>
<title d:constr="String" d:mi="5">Moby Dick</title>
<category d:constr="String" d:mi="6">fiction</category>
<author d:constr="String" d:mi="7">Herman Melville</author>
<isbn d:constr="String" d:mi="8">0-553-21311-3</isbn>
</sublist>
</book>
<Note price="8.95" d:price="Number" d:mi="13">
<title d:constr="String" d:mi="10">50</title>
<category d:constr="String" d:mi="11">reference</category>
<author d:constr="String" d:mi="12">Nigel Rees</author>
</Note>
<Note price="22.99" d:price="Number" d:mi="18">
<title d:constr="String" d:mi="14">The Lord of the Rings</title>
<category d:constr="String" d:mi="15">fiction</category>
<author d:constr="String" d:mi="16">J. R. R. Tolkien</author>
<isbn d:constr="String" d:mi="17">0-395-19395-8</isbn>
</Note>
</store>
Using the below Xpath I'm able to get the store element.
String name = "String";
String xpath = "//title[#d:constr='" + name + "']/parent::store";
But we need to get book(Anything may come which we Don't know) which is the child of store(constant word). Is it possible to do that?
Try to use below expression to match parent of required title ignoring node name:
String name = "String";
String xpath = "//title[#d:constr='" + name + "']/parent::*";
Also note that store is not parent of title, you should use "//title/ancestor::store" instead of "//title/parent::store"
If you simply want to get child of store:
//store/child::*
First child:
//store/child::*[1]
For the following xml:
<books>
<book>
<author>Peter</author>
<title>Tales from Somewhere</title>
<data>
<version>1</version>
</data>
</book>
<book>
<author>Paul</author>
<title>Tales from Nowhere</title>
<data>
<version>2</version>
</data>
</book>
</books>
How can I get the <version> value of the book author 'Paul' above, using this type of notation for building a Java XPathExpression:
//*[local-name()='books']/*
?
I used the following question as a reference:
Get first child node in XSLT using local-name()
Thanks!
This XPath will get the version of a book where there is at an author element with the value "Paul":
//book[author="Paul"]/data/version
When run against this XML:
<books>
<book>
<author>Peter</author>
<title>Tales from Somewhere</title>
<data>
<version>1</version>
</data>
</book>
<book>
<author>Paul</author>
<title>Tales from Nowhere</title>
<data>
<version>2</version>
</data>
</book>
<book>
<author>Peter</author>
<author>Paul</author>
<title>How to write a book with a friend</title>
<data>
<version>7</version>
</data>
</book>
</books>
You get this result:
<version>1</version>
<version>7</version>
In my case, I have:
<booklist>
<book id="1">
</book>
<book id="2">
</book>
<book id="3">
</book>
......
</booklist>
How can i just return:
<booklist>
<book id="1">
</book>
</booklist>
if I use /booklist/book[#id=1], I can only get
<book id="1">
</book>
But I also need the document element.
Thanks
Rather than selecting the element that you do want, try excluding the elements that you don't want.
If you are just using XPATH, this will select all of the elements except for the book elements who's #id is not equal to 1 (i.e. <booklist><book id="1" /></booklist>).
//*[not(self::book[#id!='1'])]
If you want an XSLT solution, this stylesheet has an empty template that matches all of the <book> elements that do not have #id="1", which prevents them from being copied into the output.
Everything else (document node <booklist> and <book id="1">) will match the identity template, which copies forward.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--Empty template to prevent book elements
that do not have #id="1" from being
copied into the output -->
<xsl:template match="book[#id!='1']" />
<!--identity template to copy all nodes and attributes to output -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
How can i just return:
< booklist >
< book id=1 >
< /book >
< /booklist >
XPath is a query language. Evaluating an XPath expression cannot change the structure of the XML document.
This is why the answer is: No, with XPath this is not possible!
Whenever you want to transform an XML document (which is exactly the case here), the probably best solution is to use XSLT -- a language which was designed especially for processing and transforming tree-structured data.
Here is a very simple XSLT solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="book[not(#id=1)]"/>
</xsl:stylesheet>
When this transformation is applied to the provided XML file, the wanted, correct result is produced:
<booklist>
<book id="1"/>
</booklist>
When you try to select a sub-element, only this will be returned.