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
Related
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.
I'm struggling with mapping node which can be either plain text or node which is unknown, but within set that I know all possibilities. For instance:
<some-root>
...
<data type="text/plain">10000</data>
...
<some-root>
or
<some-root>
...
<data type="...">
<date type="DateTime">2015-10-05T22:45:00.000Z</date>
</data>
...
<some-root>
As I said I know all possible tags which can be nested with <data>. That's why it would be good to somehow map them using lax=true as well (if possible)
Please do not post answer about changing structure cause this is external service. I do not have way to change it. There are already too many services which rely on that layout (but they are written by other programmers in other languages)
I'm currently working on a txt-to-xml project. Basically what I'm doing is creating different XmlElements for some of the content.
I got a DTD up and running and for now I'm creating a default xml, just to make sure every xml created is a valid xml (for the DTD given).
I'm mainly creating new Classes for every Element, which doesn't have a #PCDATA structure and it's working pretty fine so far.
Now I'm struggling with a problem:
I got the following in my DTD:
<!ELEMENT REACTION(#PCDATA | ACTOR*)>
What I'm looking for in my Text is something like:
Prof. X clapped!
and I want to extract this into my XML as:
<REACTION>
<ACTOR>Prof. X</ACTOR> clapped!
</REACTION>
So what I basically want is a String-Attribute within the ReactionClass which is devlares as XML-Element but holds an Actor-Attribute + Rest of the Text. I thought of something like:
String m_sText;
String m_sActor;
public ReactionClass(){
this.Actor = "Prof. X";
this.sText = this.m_sActor + " clapped!";
}
#XmlElement(name = "TEXT")
public String getM_sText(){ return this.m_sText; }
#XmlElement(name = "ACTOR")
public String getM_sActor(){ return this.m_sActor; }
For all other Nodes, such as the RootNode I created a RootNodeClass which holds different attributes, such as m_nLocation, m_nTime, m_nYear which are declared as XML-Elements, so the JAXB-Marshaller just builds up the XML on basis of these elements:
<ROOT>
<TIME>09:00</TIME>
<LOCATION>New York</TIME>
<YEAR>1992</YEAR>
</ROOT>
I wanted to do the same with the REACTION-Node (like mentioned above), but when creating a new Class REACTION I'm getting sth. like:
<REACTION>
<TEXT>Prof. X clapped!</TEXT>
<ACTOR>Prof. X</ACTOR>
</REACTION>
How would I put them into one Element but still keep the Tags such as above?
If anybody got an idea how to manage this I would be very thankful!
Thanks Max
First, what you most probably need is #XmlMixed. You'll probably have a structure like:
#XmlMixed
#XmlElementRefs({
#XmlElementRef(name="ACTOR", type=JAXBElement.class),
...})
List<Object> content;
With this you could put there Strings and JAXBElement<Actor> to achieve so-called mixed content.
Next, you might consider turning your DTD into XML Schema first and compiling it - or compiling the DTD with XJC.
Finally, what you have is so-called "semi-structured data" which I think is not quite suitable for JAXB. JAXB works great for strong and clear structures, but if you have mixed stuff you get weird models that are hard to work with. I can't suggest an alternative though.
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.
I am trying to parse the stack overflow data dump, one of the tables is called posts.xml which has around 10 million entry in it. Sample xml:
<?xml version="1.0" encoding="utf-8"?>
<posts>
<row Id="1" PostTypeId="1" AcceptedAnswerId="26" CreationDate="2010-07-07T19:06:25.043" Score="10" ViewCount="1192" Body="<p>Now that the Engineer update has come, there will be lots of Engineers building up everywhere. How should this best be handled?</p>
" OwnerUserId="11" LastEditorUserId="56" LastEditorDisplayName="" LastEditDate="2010-08-27T22:38:43.840" LastActivityDate="2010-08-27T22:38:43.840" Title="In Team Fortress 2, what is a good strategy to deal with lots of engineers turtling on the other team?" Tags="<strategy><team-fortress-2><tactics>" AnswerCount="5" CommentCount="7" />
<row Id="2" PostTypeId="1" AcceptedAnswerId="184" CreationDate="2010-07-07T19:07:58.427" Score="5" ViewCount="469" Body="<p>I know I can create a Warp Gate and teleport to Pylons, but I have no idea how to make Warp Prisms or know if there's any other unit capable of transporting.</p>
<p>I would in particular like this to built remote bases in 1v1</p>
" OwnerUserId="10" LastEditorUserId="68" LastEditorDisplayName="" LastEditDate="2010-07-08T00:16:46.013" LastActivityDate="2010-07-08T00:21:13.163" Title="What protoss unit can transport others?" Tags="<starcraft-2><how-to><protoss>" AnswerCount="3" CommentCount="2" />
<row Id="3" PostTypeId="1" AcceptedAnswerId="56" CreationDate="2010-07-07T19:09:46.317" Score="7" ViewCount="356" Body="<p>Steam won't let me have two instances running with the same user logged in.</p>
<p>Does that mean I cannot run a dedicated server on a PC (for example, for Left 4 Dead 2) <em>and</em> play from another machine?</p>
<p>Is there a way to run the dedicated server without running steam? Is there a configuration option I'm missing?</p>
" OwnerUserId="14" LastActivityDate="2010-07-07T19:27:04.777" Title="How can I run a dedicated server from steam?" Tags="<steam><left-4-dead-2><dedicated-server><account>" AnswerCount="1" />
<row Id="4" PostTypeId="1" AcceptedAnswerId="14" CreationDate="2010-07-07T19:11:05.640" Score="10" ViewCount="201" Body="<p>When I get to the insult sword-fighting stage of The Secret of Monkey Island, do I have to learn every single insult and comeback in order to beat the Sword Master?</p>
" OwnerUserId="17" LastEditorUserId="17" LastEditorDisplayName="" LastEditDate="2010-07-08T21:25:04.787" LastActivityDate="2010-07-08T21:25:04.787" Title="Do I have to learn all of the insults and comebacks to be able to advance in The Secret of Monkey Island?" Tags="<monkey-island><adventure>" AnswerCount="3" CommentCount="2" />
I would like to parse this xml, but only load certain attributes of the xml, which are Id, PostTypeId, AcceptedAnswerId and other 2 attributes. Is there a way in SAX so that it only loads these attributes?? If there is then how? I am pretty new to SAX, so some guidance would help.
Otherwise loading the whole thing would just be purely slow and some of the attributes won't be used anyways so it's useless.
One other question is that would it be possible to jump to a particular row that has a row Id X? If possible then how do I do this?
"StartElement" Sax Event permits to process a single XML ELement.
In java code you must implement this method
public void startElement(String uri, String localName,
String qName, Attributes attributes)
throws SAXException {
if("row".equals(localName)) {
//this code is executed for every xml element "row"
String id = attributes.getValue("id");
String PostTypeId = attributes.getValue("PostTypeId");
String AcceptedAnswerId = attributes.getValue("AcceptedAnswerId");
//others two
// you have your att values for an "row" element
}
}
For every element, you can access:
Namespace URI
XML QName
XML LocalName
Map of attributes, here you can extract your two attributes...
see ContentHandler Implementation for specific deatils.
bye
UPDATED: improved prevous snippet.
It is pretty much the same approach as I've answered here already.
Scroll down to the org.xml.sax Implementation part. You'll only need a custom handler.
Yes, you can override methods that process only the elements you want:
http://www.javacommerce.com/displaypage.jsp?name=saxparser1.sql&id=18232
http://www.java2s.com/Code/Java/XML/SAXDemo.htm
SAX doesn't "load" elements. It informs your application of the start and end of each element, and it's entirely up to your application to decide which elements it takes any notice of.