XStream corrupting values on load and save - java

My team has recently inherited a codebase that utilizes XStream 1.4.7 to load and save XML for configuration settings which then de-/serializes them from/to custom POCOs. The problem that we're having is that some of the values are getting corrupted during reads or writes. It isn't consistently occurring either which makes it that much more unusual. In most cases, it works perfectly fine with the exact same XML and the exact same POCOs.
A very simplified example (I can't post the exact code and it's quite a bit more complex so I'm going for an easy way to explain what we're seeing) is given the XML:
<monitor>
<autostart>true</autostart>
<name>MYVALUE</name>
</monitor>
Mapped to a POCO:
public class MonitorEntry {
public Boolean autostart;
public String name;
}
Loaded with XStream:
XStream xStream = new XStream(new DomDriver());
xStream.alias("Monitor", MonitorEntry.class);
Monitor monitor = (Monitor)xStream.fromXML(myFile);
The value of name in the Monitor object is read in as arVALUE instead of MYVALUE. The garbage characters at the beginning are what throws things off. Even more strangely, if we change the value of the <autostart> element to false then the XML is mapped correctly and the garbage characters do not appear.
To add to the mystery, on our end we're only seeing the corruption on loading XML to objects, but on one particular customer system they are seeing corruption only when actually saving the XML from objects. In this case, it's exactly the opposite of the above scenario. Given the same POCO with name set to MYVALUE, the actual XML written to the XML file becomes:
<monitor>
<autostart>true</autostart>
<name>arVALUE</name>
</monitor>
Now for a string value such as name it isn't much of a functional issue as it's just a name that is then just spelled wrong but where this becomes a problem is when mapping the XML value to, for example, an enum and the mapping can't find the enum value.
An example being if there is an enum:
public enum Type { VALUE1, VALUE2 };
And the POCO is:
public class MonitorEntry {
public Boolean autostart;
public String name;
public Type type;
}
With the XML:
<monitor>
<autostart>true</autostart>
<name>MYNAME</name>
<type>VALUE2</type>
</monitor>
But the XML value is being read by XStream as erLUE2 then the XStream mapping won't be able to match the correct enum value and throws an exception such as:
No enum const class com.sample.MonitorEntry$Type.erLUE2
We tried updating to XStream 1.4.8 just to see if perhaps something had been fixed but the behavior persists. The codebase is set to compile to Java 1.6 but we've tried 1.6, 7, and 8 as runtimes just to see if it was a runtime bug or something else environmental.
Has anyone else seen similar issues or have any suggestions on what might cause this? I can further update my post to include more detail if necessary. We've used XStream quite a bit before but never had issues.
Edit: We are not currently using any custom converters in this codebase, only the built-in XStream converters.

Related

How to make changes in marshalled output in Java web service client

I am trying to interact with a third party web service, who requires me to send a security token as a part of each request. The token is a node by itself, and I acquire it from the response of an initial call.
The web service endpoint is dotNet, and I have a Java client.
Apparently, the server side expects me to send the security token exactly like it was provided to me: literally the same string: so it won't do if its content has a different size, order, etc.
So, in SoapUI, everything works fine. There is a token in the response of the initial 'startSession' call, which I copy into the request of a next call.
But in Java (I tried JAX-WS and CXF generated code, both rely on JAXB) it doesn't work. I receive the token as an object after it is unmarshalled, and I use this object in the next call.
When marshalled and send, it is missing a namespace attribute in a subnode. The server side says it won't continue because the token is incorrect.
So, by using JAXB outbound logical handler functionality, I am able to add the missing namespace without any problems in the DOM source (I was also able to achieve this with a CXF interceptor).
The problem now is, that the attributes, when marshalled, are ordered in such a way that the result still not matches the provided token as it was before it was unmarshalled. Alhough it should not matter, the order of these attributes is crucial.
I have no idea how to solve this, unless it is possible to actually modify the output XML string. I even tried a dirty hack by removing all attributes from the subnode and replacing them with one attribute that visually looks the same; but then the outer two double quotes become single quotes...
I hope anyone has an idea. Because I have none.
Cheers.
UPDATE:
I should have mentioned that the attributes in question are namespace(d) attributes. The node should look like this:
<HawanedoSessionInfo xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c">
However, after using outbound JAXB handler to add the missing xmlns="...", my result looks like this:
<HawanedoSessionInfo xmlns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
In the HawanedoSessionInfo class, I used XmlType.proporder and #XmlAttribute like so:
#XmlType(name = "HawanedoSessionInfo", propOrder = {
"xsd",
"xsi",
"xmlns",
and some other non-attribute sub-elements..
private String xsd;
private String xsi;
private String xmlns;
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsd() {
return xsd;
}
public void setXsd(final String xsd) {
this.xsd = xsd;
}
#XmlAttribute(ns="http://schemas.thecompany.com/Hawanedo/Business/v2.0c")
public String getXsi() {
return xsi;
}
public void setXsi(final String xsi) {
this.xsi = xsi;
}
#XmlAttribute
public String getXmlns() {
return xmlns;
}
public void setXmlns(final String xmlns) {
this.xmlns = xmlns;
}
So apparently the proporder option does not help in this case?
UPDATE 2:
Like I wrote in my answer, it now works. Based on this LINK,
in the HawanedoSessionInfo class I added:
#XmlCustomizer(HawanedoSessionInfoCustomizer.class)
I created the customizer class exactly as described in the linked page, and I added the jaxb.properties.
So I did two things:
1) I added my attributes to (the top of the already existing) propOrder attribute. I added the attributes as instance variables and created the getters/setters. I annotated the getters with XmlAttribute.
2) I implemented the XmlCustomizer solution.
Now comes the strange part. According to Fiddler, the order of the attributes is still not changed! But I must stress that this is now working, ONLY after implementing the Customizer. What is happening here? :)
So in principle you cannot control order of attributes in a standard way, but ....
Depending on jaxb /java version the order can be determined by alphabetical order of the names, the order of declaration.
You could try in your code if a) moving the fields around changes anything, b) renaming the fields (the XMLAttribute than have to map to original name).
If you are lucky, it will work. But of course it is a hack and will work till next jaxb/java update.
The JAXB providers (the actuall implementation can have extra features), that can be used to customized the marshalling process). For example I found that: https://community.oracle.com/thread/977397 abut eclipselink.
I am sure there was a way of intercepting the soap body before it is send or governing the data serialization before it is send. I can think how it was called but try to google the jaxws client customization. If you capture the whole soap message simple xslt transforamation could fix the attributes order.
I feel your pain. The whole point of using xml, jaxws and such is to make our life easier and then someone providers decide not to follow standards and you end up with a mess that you were trying to clean for few days. Good luck and maybe try to contact xml gurus from Eclipse Moxy
I am so happy right now, because I got it working and it only cost me a full week to do so...:) With help of #Zielu, I was pointed to this link with the EclipseLink XMLCustomizer solution as suggested by Blaise Doughan: XMLCustomizer solution
I took the code in my original question (underneath 'UPDATE') and added the exact solution as suggested. Not sure if it is all necessary, but it works. Thanks guys.
It's possible you can control the order by using,
#XmlType (propOrder={"prop1","prop2",..."propN"})

JAXB Ignore 'extra' elements from Response XML

I am getting a XML response and it keeps on changing very frequently (nodes keep on increasing or reducing). After each updation in response xml my code breaks as my mapped Java class does not have all fileds.
Is there any way to avoid my code breaking if any changes occurs in response XML.
Any help will be appreciated.
Thanks.
Use JAXB.unmarshal() to simply create Java objects from XML.
By default it is very liberal.
Quoting from the javadoc:
In addition, the unmarshal methods have the following characteristic:
Schema validation is not performed on the input XML. The processing will try to continue even if there are errors in the XML, as much as possible. Only as the last resort, this method fails with DataBindingException.
So what JAXB.unmarshal() does is it tries to "transfer" as much data from XML to Java as possible, and it doesn't care if there is no Java field for an XML element or attribute, and it also doesn't care if there is a Java field for which there is no XML element or attribute.
Example
Let's try to unmarshal the following XML to an instance of java.awt.Point:
<p hi="Yo">
<y>123</y>
<peach>weor</peach>
</p>
The Java code:
String s = "<p hi=\"Yo\"><y>123</y><peach>weor</peach></p>";
Point p = JAXB.unmarshal(new StringReader(s), Point.class);
System.out.println(p); // Prints "java.awt.Point[x=0,y=123]"
We told JAXB.unmarshal() to parse a java.awt.Point instance. The input XML contains an element <y> which can be matched with Point.y so an int was parsed and set to Point.y. No XML data was found for Point.x so it was not touched. There were no match for the attribute hi and the XML element <peach>, so they were simply not used for anything.
We got absolutely no Exception here, and the most that was possible was parsed and transferred from XML to Java.
To cope with unknown fields, you can add a List<Object> property annotated #XmlAnyElement(lax=true)
#XmlAnyElement(lax = true)
private List<Object> anything;
Any elements in the input that do not correspond to explicit properties of the class will be swept up into this list. If the element is known to the JAXBContext you'll get the unmarshalled form (the #XmlRootElement annotated class or a JAXBElement<Foo>), if the element is not known to the context you'll get an org.w3c.dom.Element.
Full details in Blaise's blog.
For nodes that get removed you should be fine as long as you use types that can be null (Integer rather than int, Boolean rather than boolean, etc).

Unmarshalling a date from an XML String to entity using JAXB

When I use JAXB, there is something wrong.
I convert entity to a xml String and everything is ok.
But when I convert xml String back to entity, some information is lost (All of them have the same type java.util.Date).
In entity:
public Date flightBaseDate;
In xml:
<flightBaseDate>2013-09-16T00:00:00 08:00</flightBaseDate>
after unmarshalling, getFlightBaseDate() returns null.
I googled.
Following one suggestion, I used # in my entity.
Then it is:
#XmlElement(name = "timestamp", required = true)
public Date flightBaseDate;
I'm sure it will be perfect,
but...throws Exception, like this:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "flightBaseDate"
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.getFlightBaseDate()
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
this problem is related to the following location:
at public java.lang.String com.wonders.nlia.omms.vo.FlightServiceInfoVo.flightBaseDate
at com.wonders.nlia.omms.vo.FlightServiceInfoVo
Why JAXB could not distinguish between the property and its getMethod?
How to solve it?
Platform:jdk7 win7 eclipse tomcat wtp
My Unmarshalling code is:
JAXBContext context = JAXBContext.newInstance(FlightServiceInfoVo.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
FlightServiceInfoVo flightServiceInfoVo =(FlightServiceInfoVo)unMarshaller.unmarshal(new StringReader(flightServiceInfoVoXml));
flightServiceInfoVoXml is a String.
You can configure JAXB in many different ways. You have chosen Annotations to define the binding (this is allright, do not worry).
I strongle recommend you read about that technique first as there are a lot of pitfalls. Here is a link to a good tutorial. Here is the part in the tutorial which explains why your binding does not work: XmlAccessorType part
As for your specific issue:
In general you have to tell JAXB what and how to bind the java object to it's XML representation. If you do not do anything, then by default all public members of your class are bound (as you can read here).
Additionally you have chosen to annotate the getter method of your public member, which then just pushes the same variable twice to your XML which later causes the exception you see.
To fix your error, either specify a different mapping strategy for your class by putting e.g. (#XmlAccessorType(XmlAccessType.NONE)) before your class declaration or move the annotation from the getter method to the property.
By the way: Having a getter method and a public member variable does not make sense at all. So making your member variable private will also fix your issue with JAXB and be a lot better for your class design.
the exception clearly says that the property name is duplicated, so check you class for a property 'flightBaseDae', it should be unique. remove the duplicate then unmarshall it

#XmlIDREF not seeing hierarchy when marshalling to XML

I'm very new at XML and I'm having a problem I'm not able to solve and after looking around for hours I've decided to post my problem.
I'm using #XmlIDREF to just have the XmlID of some java classes on the XML doc.
All works fine, but when JAXB looks for the ID depeen on the tree hierarchy it seems that it couldn't find it and marhsalls the whole object again.
I'll show you the (simplified) model, it's all about optical routers, java classes represent the router and some components:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class OpticalRouter {
// #XmlID inherited from upper class
private List<FiberConnection> fiberConnections = new ArrayList<FiberConnection>();
}
That's a fiber connection:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class FiberConnection {
#XmlID
private String instanceID;
#XmlIDREF
Card card;
#XmlIDREF
Port port;
#XmlIDREF
Channel channel;
}
And finally:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({
DropCard.class,
AddCard.class
})
public class Card{
// #XmlID inherited from supper class
}
All works fine when I marshall ports and channels due they don't have subclasses.... but when it's time to marshall the cards if it's an AddCard or a DropCard it marshalls it another time instead of using the IDREF.
It seems that JAXB doesn't find them on the hierarchy...
If I change the original "Card card;" of FiberConnection for an "AddCard" for example it works too (JAXB finds the IdREF and doesn't marshall it again).
Hope I have explained that clearly.
Ask if not, I'll be glad to answer :)
Thanks in advance!
EDITED
Ok, I've come back with new info and results to explain myself better.
Due it's a huge class model and I don't want to make the post to much difficult to read I have created a kind of UML class diagram to make it much easier to read with some important info that should help (XML annotations and parameters). I have also included #XmlElement tags as Blaise advised me (thanks again).
Here you can find the yEd UML archive: yEd file
And there an UML jpg if you don't have/want to download graph
editor: jpg file
I also include (to finish) a part of the XML to better see what's happening.
Here I have a fiberConnection as the above mentioned.
I have a scCard and a fiberChannelPlan that have already been marshalled before on the XML doc (checked) but they are being marshalled again...
In exchange, srcPort, srcChannel and fiberChannels, also marshalled before, have only their ID's.
<OpticalRouter>
<fiberConnections>
<instanceID>FiberConnection#29e83b01</instanceID>
<srcCard xsi:type="DropCard" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<instanceID>DropCard#6b02b23d</instanceID>
<type>11</type>
<subtype>1</subtype>
<chasis>0</chasis>
<fiberChannelPlan xsi:type="ChannelPlan">
<instanceID>ChannelPlan#7e246b6d</instanceID>
<firstChannel>0</firstChannel>
<lastChannel>0</lastChannel>
<maxFreq>196.1</maxFreq>
<minFreq>191.6</minFreq>
<fiberChannels>Channel/360</fiberChannels>
<fiberChannels>Channel/368</fiberChannels>
<fiberChannels>Channel/376</fiberChannels>
<fiberChannels>Channel/384</fiberChannels>
</fiberChannelPlan>
<cardExpressPort>Port#4f781d1d</cardExpressPort>
<carCommonPort>Port#56bf83ad</carCommonPort>
</srcCard>
<srcPort>Port#56bf83ad</srcPort>
<srcChannel>Channel/184</srcChannel>
</fiberConnections>
</OpticalRouter>
I think that the problem is something related to that post I found (even if I'm using linux and java 1.6) or other posts I have seen here on StarckOverflow:
java.net/jira/browse/JAXB-870
Thanks in advance!
P.S: all code I'm using is opensource and can be downloaded from the main source at a git repository if someone thinks it should be easier for him.
You need to make sure that each object in your graph is referenced through a containment/nesting relationship (such as #XmlElement), this forms the XML. I don't see where this requirement is met in your model. Then you can use #XmlID/#XmlIDREF to have key based relationships within the tree to turn it into a graph.
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html

JAXB marshal Set<Object>

I have an object similar to this:
public class Obj {
#XmlElement(name="value")
public Set<Object> values;
}
When marshaling, this is generating an xml like:
<Obj>
<value xsi:type="xs:dateTime" xmlns:xs="http://www.w3.org/2001/XMLSchema">2009-02-14T00:31:30.001+01:00</value>
<value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">test</value>
</Obj>
However, I want to change some of that values (like the date format used for serializing Date and Timestamp objects), and also get rid of the annoying xsi attributes (but this is not really a requirement, I can live with that)
I've tried adding a #XmlJavaTypeAdapter to values, but in the adapter I get the full Set<Object> to adapt, instead of single elements. I've also tried with a package adapter, but, as my Set is for Object, I cannot put the #XmlJavaTypeAdapter(type) attribute.
Also, I've tried with #XmlJavaTypeAdapter(value=MyAdapter.class, type=Timestamp.class) to get only an adapter for the values inside that Object that I want.
So the question is, does someone know a way to get an adapter to work for this? Or maybe, change the date format every time a Date or Timestamp object is serialized?
Thanks in advance!
#XmlJavaTypeAdapter with the type property has to be specified on the package level. When used in this way it indicates that all usages of that type within the specified package are converted using the XmlAdapter. E.g. if you have a package-info.java like
#XmlJavaTypeAdapters({
#XmlJavaTypeAdaptor(type=Timestamp.class, value=MyAdapter.class)
})
package org.example;
Then a class in that package with a Timestamp field.
package org.example;
public class Obj {
public Timestamp aTimestamp;
}
The specified adapter will be used to convert the timestamp. I suspect that this will work for your Set<Object> case but I haven't tried it myself.
The reason for the xsi:type attribute is that JAX-B likes to produce XML it can deserialize, so it needs to indicate what type it is or it could only parse everything back as strings. You can get rid of this attribute by using the #XmlElementRef annotation to create a schema substitution group, but in this case the XML will be produced with different element names. E.g.
public class Obj {
#XmlElementRefs({
#XmlElementRef(type=String.class, name="string"),
#XmlElementRef(type=Timestamp.class, name="timestamp")
})
public Set<Object> value;
}
Would produce the following XML structure if you had a timestamp and a string in the set. In this scenario the xsi:type attribute is unnecessary since JAX-B can tell what type to create from the element name.
<Obj>
<timestamp>2009-02-14T00:31:30.001+01:00</timestamp>
<string>test</string>
</Obj>
I would strongly recommend using the #XmlElementWrapper annotation to wrap up all the set items if you're going to take this approach.
If all you're after is a simple set of strings that you don't care about deserializing back to Java (or any other) objects with the correct types, then the simplest solution is to have an XmlAdapter that does just adapt the full Set<Object> into a Set<String> and handle the conversion yourself.

Categories