How get attribute content by another attribute value with XPath? - java

I have XML like:
<?xml version='1.0' encoding='UTF-8'?>
<ClinicalDocument xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='urn:hl7-org:v3'
xmlns:ext='urn:hl7-RU-EHR:v1'
xsi:schemaLocation='urn:hl7-org:v3'>
<author>
<time value='20160809000000+0300'/>
<assignedAuthor>
<id root='1.2.643.5.1.13.3.25.1.1.100.1.1.70' extension='1'/>
<id root='1.2.643.100.3' extension='03480134121'/>
<id nullFlavor='NI'/>
</assignedAuthor>
</author>
</ClinicalDocument>
I have to get extension in id with root's value = 1.2.643.100.3.
I must use XPath 2.0.
I have tried:
*[name()='ClinicalDocument']/*[name()='author']/*[name()='assignedAuthor']/*[name()='id' and #id='1.2.643.100.3']/#extension. Not working
/*[name()='ClinicalDocument']/*[name()='author']/*[name()='assignedAuthor']/*[name()='id'][2]/#extension, but order of ids can
mixed. So that, I should retrieve by id's value
It's needed to me for retrieving value by Java's XPathExpression

First, bind namespace prefix, u: to urn:hl7-org:v3.
Then, this XPath,
//u:id[#root='1.2.643.100.3']/#extension
will return 03480134121, as requested.
If you are unable to bind a namespace prefix, you can instead use this XPath,
//*[local-name() ='id' and #root='1.2.643.100.3']/#extension
which will also return 03480134121, as requested.

Correct XPath: /*[name()='ClinicalDocument']/*[name()='author']/*[name()='assignedAuthor']/*[local-name()='id' and #root='1.2.643.100.3']/#extension

Related

JDOM2 xpath finding nodes within a different namespace

I'm attempting to use JDOM2 in order to extract the information I care about out of a XML document. How do I get a tag within a tag?
I have been only partially successful. While I have been able to use xpath to extract <record> tags, the xpath query to extract the title, description and other data with in the record tags has been returning null.
I've been using Xpath successfully to extract <record> tags out of the document. To do this I use the follwing xpath query: "//oai:record" where the "oai" namespace is a namespace I made up in order to use xpath.
You can see the XML document I'm parsing here, and I've put a sample below: http://memory.loc.gov/cgi-bin/oai2_0?verb=ListRecords&set=cwp&metadataPrefix=oai_dc
<record>
<header>
<identifier>oai:lcoa1.loc.gov:loc.pnp/cph.3a02293</identifier>
<datestamp>2009-05-27T07:22:37Z</datestamp>
<setSpec>cwp</setSpec>
<setSpec>lcphotos</setSpec>
</header>
<metadata>
<oai_dc:dc xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
<dc:title>Jubal A. Early</dc:title>
<dc:description>This record contains unverified, old data from caption card.</dc:description>
<dc:date>[between 1860 and 1880]</dc:date>
<dc:type>image</dc:type>
<dc:type>still image</dc:type>
<dc:identifier>http://hdl.loc.gov/loc.pnp/cph.3a02293</dc:identifier>
<dc:language>eng</dc:language>
<dc:rights>No known restrictions on publication.</dc:rights>
</oai_dc:dc>
</metadata>
</record>
If you look in the larger document you will see that there is never a "xmlns" attribute listed on any of the tags. There is also the matter of there being three different namespaces in the document ("none/oai", "oai_dc", "dc").
What is happening is that the xpath is matching nothing, and evaluateFirst(parent) is returning null.
Here is some of my code to extract the title, date, description etc. out of the record element.
XPathFactory xpf = XPathFactory.instance();
XPathExpression<Element> xpath = xpf.compile("//dc:title",
Filters.element(), null,
namespaceList.toArray(new Namespace[namespaceList.size()]));
Element tag = xpath.evaluateFirst(parent);
if(tag != null)
{
return Option.fromString(tag.getText());
}
return Option.none();
Any thoughts would be appreciated! Thanks.
In your XML, dc prefix mapped to the namespace uri http://purl.org/dc/elements/1.1/, so make sure you declared the namespace prefix mapping to be used in the XPath accordingly. This is part where the namespace prefix declare in your XML :
<oai_dc:dc
xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/
http://www.openarchives.org/OAI/2.0/oai_dc.xsd">
XML parser only see the namespace explicitly declared in the XML, it won't try to open the namespace URL since namespace is not necessarily a URL. For example, the following URI which I found in this recent SO question is also acceptable for namespace : uuid:ebfd9-45-48-a9eb-42d

Jdom2 Sharepoint XML Fields

Having some trouble returning certain fields from a SharePoint List SOAP request.
Here is the XML:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:soap1="http://schemas.microsoft.com/sharepoint/soap/">
<soap:Header/>
<soap:Body>
<soap1:UpdateListItems>
<soap1:listName>69A3FFFA-782B-45D5-B776-2BE6D5645745</soap1:listName>
<soap1:updates>
<Batch OnError="Continue">
<Method ID="1" Cmd="New">
<Field Name="Title">New Item</Field>
</Method>
</Batch>
</soap1:updates>
</soap1:UpdateListItems>
</soap:Body>
</soap:Envelope>
I am able to use the following Jdom2 code to grab certain values like this:
// set your name spaces.
Namespace soap = Namespace.getNamespace("soap","http://www.w3.org/2003/05/soap-envelope");
Namespace soap1 = Namespace.getNamespace("soap1","http://schemas.microsoft.com/sharepoint/soap/");
// drill down into elements
Element rootNode = doc.getRootElement();
// Get Body node
Element body = rootNode.getChild("Body",soap);
// Get UpdateListItem Element
Element UpdateListItems = body.getChild("UpdateListItems",soap1);
// Get updates node
Element updates = UpdateListItems.getChild("updates",soap1);
// Set list name as String variable
String listNameString = UpdateListItems.getChild("listName",soap1).getText();
// Print list text value ** THIS WORKS**
System.out.println(listNameString);
However, I can't seem to figure out how to select the Field elements.
For example: How would I select the "Title" Field?
<Field Name="Title">New Item</Field>
UPDATE:
I also able to get the attribute "Name" from the "Field" element, but can only return or set the name of value of the attribute. I need to be able to access the test within the "Field" Element.
I can get the value of the attribute like this:
System.out.println(field.getAttribute("Name").getValue()); // Prints Title
And I can get the name like this:
System.out.println(field.getAttribute("Name").getName()); // Prints Name
But, I need to be able to return the text value of the element.
UPDATE 2:
I didn't mention. The XML really looks like this:
` <?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:soap1="http://schemas.microsoft.com/sharepoint/soap/">
<soap:Header/>
<soap:Body>
<soap1:UpdateListItems>
<soap1:listName>69A3FFFA-782B-45D5-B776-2BE6D5645745</soap1:listName>
<soap1:updates>
<Batch OnError="Continue">
<Method ID="1" Cmd="New">
<Field Name="Title">New Item</Field>
<Field Name="Classification" Type="Choice">Funny</Field>
<Field Name="Title">New Item</Field>
<Field Name="Title" Type="Text">Funny List Item</Field>
</Method>
</Batch>
</soap1:updates>
</soap1:UpdateListItems>
</soap:Body>
</soap:Envelope>`
I can submit this via SoapUI to SharePoint and it works. But if there are multiple "Field" elements, with different attributes, how can I select the correct one via Jdom2?
I can do this:
String title = field.getText(); //returns New Item
But how would I be able to grab the text from other "Field" elements that use the "Name" attribute?
It is all in the namespaces. You have three of them, soap, soap1, and there's also the default namespace, which, in this case, is "". JDOM designates this namespace as Namespace.NO_NAMESPACE.
So, to get the Field Element from the updates Element, you can do:
Element methods = updates.getChild("Method", Namespace.NO_NAMESPACE);
Element field = methods.getChild("Field", Namespace.NO_NAMESPACE);
These can be made simpler, if you want, by using the getChild method that does not have the namespace parameter at all, like:
Element methods = updates.getChild("Method");
Element field = methods.getChild("Field");
The important thing to see here, is that your document has 3 namespaces, and that the Field element (and Method too) are not in the soap, or soap1 namespace.
Thanks for the help rolfl. I figured it out. You can loop through the Child elements to access the different "Field" attributes. I then test for the attribute name to get or set its content. This is the best I could come up with.
for (Element node : method.getChildren("Field")){
if(node.getAttributeValue("Name").equalsIgnoreCase("Title")){
node.setText("String");
}
System.out.println(node.getAttribute("Name").getValue());
}

How to compare an value using xpath?

i am currently doing java and html, i have to check some values from the website against my database. i would like to ask if XPATH has such function like "is text present" or "get text = data" rather than get element, is there something to check if the value is there or something?
You can use boolean method in XPath.
For example, lets say your XML is this
<?xml version="1.0" >
<persons>
<person>
<name>Peter</name>
<sex>MALE</sex>
<age>25</age>
</person>
</persons>
You can use the boolean method as,
boolean(/persons/person[name = 'Peter'])

Xml id attribute to work with Java's getElementById? [duplicate]

This question already has answers here:
Java DOM getElementByID
(2 answers)
Closed 3 years ago.
I have an xml document being parsed in Java as a w3c document.
In my xml, i have many elements of the same name, e.g <item ..... />, each one with unique attribute's value, e.g <item name="a" .... />.
I want in java to do:
doc.getElementById("a")
in order to get that specific item I have there with that name.
How can I tell java to use 'name' as the id?
Or, alternately, How can I fetch that specific item in least complexity?
DOM is not the best API to easily query your document and get back found elements. Learn XPath, which is a more appropriate API, or iterate through the tree of elements by yourself.
getElementById() will only return the element which has the given id attribute (edit: marked as such in the document DTD or schema). It can't find by name attribute.
See Java XML DOM: how are id Attributes special? for details.
You need to write a DTD that defines your attribute as being of type ID.
Well, To make a complete answer, I had to use DTD schemas like everyone stated.
Since my needs are quite simple, I added it in embedded in my xml the following way:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ATTLIST item
name ID #REQUIRED
>
]>
<root> .... </root>
The only important thing left to know is that once you declare the ATTLIST, I have to declare all of the rest of my attributes, therefore, you need to add IMPLIED:
some-attribute CDATA #IMPLIED
It says that some-attribute contains some data (can use also PCDATA for parsed cdata), and is implied, which means, it can be there or it cannot. doesnt matter.
So eventually, it'll look something like:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
<!ATTLIST item
name ID #REQUIRED
some-attribute CDATA #IMPLIED
>
]>
<root> .... </root>
And from Java side, Just use it blindly, e.g getElementById("some-name")
In order to make doc.getElementById("a") work you need to change your XML to <item id="a" name="a" .... />
If you can't change the XML, you could use XPath to retrieve this element.

Select a node using xpath and jdom

I have an xform document
<?xml version="1.0" encoding="UTF-8"?><h:html xmlns:h="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:jr="http://openrosa.org/javarosa">
<h:head>
<h:title>Summary</h:title>
<model>
<instance>
<data vaultType="nsp_inspection.4.1">
<metadata vaultType="metadata.1.1">
<form_start_time type="dateTime" />
<form_end_time type="dateTime" />
<device_id type="string" />
<username type="string" />
</metadata>
<date type="date" />
<monitor type="string" />
</data>
</instance>
</model>
</h:head>
I would like to select the data element from the xform using xpath and jdom
XPath xpath = XPath.newInstance("h:html/h:head/h:title/");
seems to work fine and selects the title element but
XPath xpath = XPath.newInstance("h:html/h:head/model");
does not select the model element.
I guess it has something to do with the namespace.
A few things. You really should be using JDOM 2.0.x ... (2.0.5 is latest release). The XPath API in the 2.0.x versions is far better than the one in JDOM 1.x: see https://github.com/hunterhacker/jdom/wiki/JDOM2-Feature-XPath-Upgrade
#wds is right about not having the correct namespace for the xforms elements too.... and that is why you XPath is working, because it has the same namespace as the xhtml elements with the 'h' prefix. Your code is likely to be broken still.
Namespaces in XPaths often confuse people, because every namespace in an XPath has to have a prefix. Even if something is the default namespace in the XML (no prefix like your 'model' element), it has to have one in the XPath. queries with no prefix in the XPath always reference the 'no namespace' namespace.... (XPath specification: http://www.w3.org/TR/xpath/#node-tests )
A QName in the node test is expanded into an expanded-name using the namespace
declarations from the expression context. This is the same way expansion is done
for element type names in start and end-tags except that the default namespace
declared with xmlns is not used: if the QName does not have a prefix, then the
namespace URI is null (this is the same way attribute names are expanded). It is
an error if the QName has a prefix for which there is no namespace declaration in
the expression context
Assuming #wds is correct, and the namespace for the model element is supposed to be "http://www.w3.org/2002/xforms" then your namespace delcaration in your document should be xmlns="http://www.w3.org/2002/xforms". But, this namespace is the 'default' namespace, and the URI for the no-prefix namespace in your XPath query is "".
To access the http://www.w3.org/2002/xforms namespace in your XPath you have to give it a prefix fo the context of the XPath, let's say xpns (for xpath namespace). In JDOM 1.x you add that namespace with:
XPath xpath = XPath.newInstance("/h:html/h:head/xpns:model");
xpath.addNamespace(Namespace.getNamespace("xpns", "http://www.w3.org/2002/xforms");
Element model = (Element)xpath.selectSingleNode(mydoc)
Note how that adds the xpns to the query. Also, note that I have 'anchored' the h:/html reference to the '/' root of the document, which will improve the performance of the query evaluation.
IN JDOM 2.x, the XPath API is significanty better (even though in some cases it may seem overkill).
XPathFactory xpf = XPathFactory.instance();
XPathExpression<Element> xpath = xpf.compile("/h:html/h:head/xpns:model",
Filters.element(), null,
Namespace.getNamesace("xpns", "http://www.w3.org/2002/xforms"));
Element model = xpath.evaluateFirst(mydoc);
See more about the new XPath API in the JDOM 2.x javadoc: XPathFactory.compile(...) javadoc

Categories