So I have the following input, expected output and actual output xml:
input.xml
<Request>
<EmailSubjectLine>Main Contact & No Reported To</EmailSubjectLine>
<ProductRq>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
</ProductRq>
<!-- rest of input -->
</Request>
expected-output.xml
<ProductRq xmlns="http://test.org/standards/intake">
<Audit>
<TransID>Test</TransID>
</Audit>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<RqUID>Test</RqUID>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
<!-- rest of expected-output -->
</ProductRq>
actual-output.xml
<ProductRq xmlns="http://test.org/standards/intake">
<Audit>
<TransID>123534Abwe-asdcv-1258qw-asd</TransID>
</Audit>
<Signon>
<ClientDt>1/6/2017 11:25:45 AM</ClientDt>
<CustLangPref>en-US</CustLangPref>
</Signon>
<SvcRq>
<RqUID>xxxxxxxx-2802-xxxx-xxxx-bf8361xxxxxx</RqUID>
<NotificationRq>
<RqUID>CG-17Dawe-12354-Hw35Sf</RqUID>
<TransactionRequestDt>2017-01-06</TransactionRequestDt>
<Currency>USD</Currency>
</NotificationRq>
</SvcRq>
<!-- rest of actual-output -->
</ProductRq>
I'm comparing them with the following Diff set up:
MyTest.java
Diff diff = DiffBuilder
.compare(xmlExpectedOutput)
.withTest(xmlOutput)
.normalizeWhitespace()
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
.whenElementIsNamed("Audit")
.thenUse(ElementSelectors.byXPath("./TransID", ElementSelectors.byName))
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
.elseUse(ElementSelectors.byNameAndText)
.build()
))
.checkForSimilar()
.build();
I get the following differences when I run the above input and compare with expected-output.xml:
[Expected child '{http://test.org/standards/intake}RqUID' but was 'null' - comparing <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] to <NULL> (DIFFERENT), Expected child 'null' but was '{http://test.org/standards/intake}RqUID' - comparing <NULL> to <RqUID...> at /ProductRq[1]/SvcRq[1]/NotificationRq[1]/RqUID[1] (DIFFERENT)]
I don't get why my Element selector wouldn't work, am I using it incorrectly? My aim is whenever TransmissionId or NotificationRq/RqUID are found, to match them with the expected output versions by name only, otherwise use name and text for other elements as these elements contain unique generated ids that change every test run and can't be predicted(with a view to creating a more complex selector later, e.g. to compare ProductRq via name and attribute as a namespace is added to this). Is there something I'm missing, and am I able to combine the 2 XPath selectors together rather than several when/then lines and the default case?
Note: the xml is transformed via xslt. The namespace on PRoductRq is not there on the source document; the source is copied, the namespace added to ProductRq and then sent for output along with some element removals/modifications/additions
XMLUnit says the RqUID elements inside the NotificationRq wouldn't match and of course they are different.
.whenElementIsNamed("NotificationRq")
.thenUse(ElementSelectors.byXPath("./RqUID", ElementSelectors.byName))
means: when XMLUnit tries to find a partner for an NotificationRq element then it has to search for an NotificationRq that has an RqUID child - and only use the RqUID element.
It doesn't set up any rules for any other element, in particular RqUID itself. For RqUID elements the default rules apply and
.elseUse(ElementSelectors.byNameAndText)
says: XMLUnit only accepts two elements as pairs if their names and the nested text match. Which is not the case for the RqUID elements in question.
Your whole ElementSelector says
match Audits if they have TransID children of arbitrary content.
match NotificationRqs if they have RqUID of arbitrary content.
use element name and nested text otherwise
which doesn't fit your example. Looking at your XML you probably wanted
match almost everything by element name and nested text (although from the example the element name would be enough)
ignore the nested text of TransId children of Audits
ignore the nested text of RqUID children of NotificationRq
There is no built-in predicate for "element named foo if it is a child of an element named bar", it could be something like
Predicate<Element> transIdInAudit = e -> {
if (e == null || e.getParentNode() == null) {
return false;
}
return "TransID".equals(e.getLocalName()) && "Audit".equals(e.getParentNode().getLocalName());
};
which you likely want to make generalizable :-)
With that you'd use
.withNodeMatcher(new DefaultNodeMatcher(ElementSelectors.conditionalBuilder()
.when(transIdInAudit)
.thenUse(ElementSelectors.byName)
.when(rqUIDInNotificationRq) // similar to transIdInAudit
.thenUse(ElementSelectors.byName)
.elseUse(ElementSelectors.byNameAndText)
.build())
Maybe you really want to match SvcRq if they have matching RqUID, maybe not. If so you'd use the structure you currently use for NotificationRq.
This in itself will not be enough to ignore the nested text of the matched TransId and RqUID elements, it will only ensure XMLUnit will pick the nodes you want it to use. For the nested text you'll need a DifferenceEvaluator.
Given that you are using ElementSelectors.byNameAndText by default, you know the nested texts are the same for all matched nodes except for the two specific elements where you want to ignore the content. So a DifferenceEvaluator like
DifferenceEvaluators.chain(DifferenceEvaluators.Default,
DifferenceEvaluators.downgradeDifferencesToEqual(ComparisonType.TEXT_VALUE))
should work.
Related
I am trying to use JAXB to unmarshal an XML file that has an element that occurs 5 times, but not in a row; I want to make some changes, then marshal it back to XML. When written back to the XML file, the instances of the element need to go back in the same order, and be separated by the same intervening elements as before
I know I can represent an element that occurs multiple times with a Collection, and I can specify the order of fields using #XmlType( propOrder = { ... } ), but I can't figure out to do both at the same time...
I tried using 5 different field names in my Java class (encryptedData1, encryptedData2, ...), and 5 different pairs of getters/setters, and then annotating the setters with the same name:
#XmlElement( name = "EncryptedData" )
but when I unmarshal, only the first one gets set, the others are all null. The field that does get filled has the value of the last instance in the XML file, so I'm guessing it's just getting set five times
If I use a List, then when I write out to the XML file, they all get written together
Here is a sample of the original XML; the EncryptedData element is the one in question:
<NodeInformation>
...
<ProxyIpPort>1194</ProxyIpPort>
<LicenseKeyID />
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<CipherData>
<CipherValue>************************</CipherValue>
</CipherData>
</EncryptedData>
<ProxyUsername />
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<CipherData>
<CipherValue>***********************</CipherValue>
</CipherData>
</EncryptedData>
<ActualIpAddress />
<HasMASServer>false</HasMASServer>
<MASServerIpAddress />
<MASServerWebListeningPort>443</MASServerWebListeningPort>
<ModemNumber />
<RememberLoginPassword>true</RememberLoginPassword>
<LoginName>admin</LoginName>
<EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<CipherData>
<CipherValue>***************************</CipherValue>
</CipherData>
</EncryptedData>
...
</NodeInformation>
Thank you in advance for any insight
Do you only want to change elements != EncryptedData, i.e. keep the unchanged EncryptedData elements in their relative positions?
If this is the case, it might be possible using JAXB Binder,
see JAXB & XML Infoset Preservation
My Scenario : I may get the different outputs which is shown below.I want to retrieve the "Units" tag value depends on Code tag.
Output1 :
<Riders>
<Rider>
<Name>ALSP</Name>
<Code>ALSP</Code>
<Units>3</Units>
</Rider>
<Rider>
<Name>Individual</Name>
<Code>Select Type of Coverage</Code>
<OptionCode>Individual</OptionCode>
<IsFeature>true</IsFeature>
</Rider>
</Riders>
Output2 :
<Riders>
<Rider>
<Name>AFO</Name>
<Code>AFO</Code>
<Units>6</Units>
</Rider>
<Rider>
<Name>Individual</Name>
<Code>Select Type of Coverage</Code>
<OptionCode>Individual</OptionCode>
<IsFeature>true</IsFeature>
</Rider>
</Riders>
I have tried below xpath but didn't worked out. Could anyone suggest me.
/Riders/Rider/Code[text()[contains(.,'AFO')] or text() [contains(.,'ALSP')]]/Units
Depending on if you want to get the node with regards to the root or just find any matching node, you could use something like...
*/Rider[Code[contains(.,'ALSP')]]/Units
Which will return you all the Units nodes which belong to any Rider node, which have a Code node, whose text contains ALSP
Of course you could also use...
*/Rider[Code[text() = 'ALSP']]/Units
If the Code must match exactly.
The above will find all the Units nodes of the Rider node anywhere in the document. If the position is important, you would need to replace */ with /Riders/ instead.
Now, if you want to find both ALSP and AFO, you could use something like...
*/Rider[Code[text() = 'ALSP' or text() = 'AFO']]/Units
I do REST calls to a WebService and receive always XML as response. Then i'm parsing that XML und filling Java objects with those informations.
The Problem is that the element-tags could have different namespaces, like this:
<ns:title>....</ns:title>
or
<ns2:title>....<ns2:title>
or
<title>...<title>
EDIT:
And the namespace URIs look like this:
<ns2:feed xmlns="http://www.example.com/routing1/routing2"
xmlns:ns2="http://www.w3.org/../Atom"
xmlns:ns3="http://www.example.com/routing1/routing2"
xmlns:ns4="http://purl.org/routing1/routing2/1.0">
So therefore i changed the method element.getElementsByTagNameNS("specifiedNamespace", "title") to element.getElementsByTagNameNS("*", "title").
Is that okay to match all namespace, because i have also the case that the element-tag doesn't have a namespace like the third example <title>..</title>..
Is there a better procedure, to solve that problem? Or is it okay to solve it like, how i do it?
Thanks.
EDIT: 2 response examples
1.
<ns2:feed xmlns="http://www.example.com/routing1/routing2" xmlns:ns2="http://www.w3.org/../Atom" xmlns:ns3="http://www.example.com/routing1/routing2" xmlns:ns4="http://purl.org/routing1/routing2/1.0">
...
<ns2:someTag1>..</ns2:someTag1>
<ns2:title>title</ns2:title>
<entry>...</entry>
....
</ns2:feed>
2
<ns2:feed xmlns="http://www.w3.org/../Atom" xmlns:ns2="http://www.example.com/routing1/routing2" xmlns:ns3="http://www.example.com/routing1/routing2" xmlns:ns4="http://purl.org/routing1/routing2/1.0">
...
<someTag1>..<someTag1>
<title>title<title>
<ns2:entry>...</ns2:entry>
....
</ns2:feed>
Your title elements have the same namespace in both of your examples.
In the first example, you have:
xmlns:ns2="http://www.w3.org/../Atom"
and
<ns2:title>title</ns2:title>
so this means that title is in the http://www.w3.org/../Atom namespace.
In the second example, you have:
xmlns="http://www.w3.org/../Atom"
and
<title>title<title>
so here again title is in the http://www.w3.org/../Atom namespace.
The prefixes are different (the second example isn't using a prefix for title), but the namespace is the same.
This means that you should be able to use:
element.getElementsByTagNameNS("http://www.w3.org/../Atom", "title")
and it should successfully select the title element, even if the prefixes change.
Given XML like this:
...
<Sport SportId="1">
<Name language="en">Soccer</Name>
<Name language="fi">Jalkapallo</Name>
...
</Sport>
...
How can I, using the Simple XML Framework, read the two values into fields in a Java class? (The <Sport> element is already correctly mapped to the corresponding class.)
public class Sport {
...
String nameEn;
String nameFi;
...
}
I've tried approaches like:
#Element(name = "Name")
#Path("Name[#language='en']")
String nameEn;
But the parsing fails with:
Exception in thread "main" org.simpleframework.xml.core.PathException:
Invalid index for path '[#language='en']' in field 'nameEn'
Also, omitting #Element like this:
#Path("Name[#language='en']")
String nameEn;
...parsing doesn't crash, but nameEn value stays null.
I'd like the matching to be based on the language attribute (instead of ordering), but I'm wondering if that's possible (maybe XPath support in Simple Framework is limited?).
Have you tried getting the text of the element explicitly? i.e. Name[#language='en']/text()
Your Xpath is selecting the element, not the text of the element, which can cause some XML engines to choke.
Okay, to clarify, I have an XML/RDF file that describes data with a natural categorical tree structure (like folders and files). The data is not structured in a tree, rather, there is information that explains how to rebuild the tree (namely the nested set values of each node). I am starting with no knowledge other than the assumption that some statement in the file has a RootTree property who's object is the URI of the statement describing the root node of the tree.
Obtaining that object is easy, I simply use:
// Obtain the node describing the root of the Pearltree.
mRootProp = mModel.createProperty(Pearltree.RDF.PearlTreeNS, "rootTree");
NodeIterator roots = mModel.listObjectsOfProperty(mRootProp);
Now, I am further able to list all statements which have the property pt:parentTree and the object roots.nextNode():
StmtIterator sit = mModel.listStatements(null, RDF.ParentTree, rootNode);
This gives me a list of all such statements. These statements are part of elements that look like such in the RDF/XML file (note these have a different parentTree value but appear in the same context):
<pt:RootPearl rdf:about="http://www.pearltrees.com/dcow/pearltrees-videos/id5296268#rootPearl">
<dcterms:title><![CDATA[Pearltrees videos]]></dcterms:title>
<pt:parentTree rdf:resource="http://www.pearltrees.com/dcow/pearltrees-videos/id5296268" />
<pt:inTreeSinceDate>2012-06-11T20:25:55</pt:inTreeSinceDate>
<pt:leftPos>1</pt:leftPos>
<pt:rightPos>8</pt:rightPos>
</pt:RootPearl>
<pt:PagePearl rdf:about="http://www.pearltrees.com/dcow/pearltrees-videos/id5296268#pearl46838293">
<dcterms:title><![CDATA[why Pearltrees?]]></dcterms:title>
<dcterms:identifier>http://www.youtube.com/watch?v%3di4rDqMMFx8g</dcterms:identifier>
<pt:parentTree rdf:resource="http://www.pearltrees.com/dcow/pearltrees-videos/id5296268" />
<pt:inTreeSinceDate>2012-06-11T20:25:55</pt:inTreeSinceDate>
<pt:leftPos>2</pt:leftPos>
<pt:rightPos>3</pt:rightPos>
</pt:PagePearl>
...
Now, what I would like to do is obtain a reference to all statements with subject sit.nextStatement()'s subject. In this example:
"http://www.pearltrees.com/dcow/pearltrees-videos/id5296268#rootPearl"
and
"http://www.pearltrees.com/dcow/pearltrees-videos/id5296268#pearl46838293"
My goal is to obtain the content of each element including its rightPos and leftPos so I can reconstruct the tree.
You can simplify your code somewhat as follows:
mRootProp = mModel.createProperty(Pearltree.RDF.PearlTreeNS, "rootTree");
Resource root = mModel.listResourcesWithProperty( mRootProp ).next();
This assumes you know you have exactly one root per model. If that might not be true, modify the code accordingly.
The method:
getSubject()
of a Statement will return the Subject as a Resource. You can then use the
getProperty(Property p)
method of the returned Resource to obtain the Statements that include the property in question.
So, in my case, I use:
Resource r;
Statement title, id, lpos, rpos;
while(sit.hasNext()) {
r = sit.nextStatement().getSubject();
title = r.getProperty(DCTerms.title);
id = r.getProperty(DCTerms.identifier);
lpos = r.getProperty(PearlTree.RDF.leftPos);
rpos = r.getProperty(PearlTree.RDF.rightPos);
...
}