JAXB XML repeating alternating XmlElements without Parent element - java

We have been using JAXB to generate XML to interface with a third party. This third party is asking that for one section we produce a set of 2 different 0-n XML Elements in a repeating fashion without parent-elements separating them. Here is an example of whats requested:
<education>
<code>ENG24</code>
<percentage>25</percentage>
<code>ENG25</code>
<percentage>20</percentage>
<code>SPA50</code>
<percentage>30</percentage>
<code>SPA60</code>
<percentage>25</percentage>
</education>
I cannot figure out a way to represent this type of XML with JAXB Java XML Binding. Is it at all possible to represent the above XML with JAXB Java XML Binding?
I am aware that the XML above is poorly designed but I cannot change the third party's mind to use and tags instead.
If JAXB XML binding is not going to work that I would be very thankful for suggestions of what library/tool to use instead to produce XML and to do the marshal/un-marshaling.
Thanks!
Matt

Yes, it is possible. The simplest would be probably to use #XmlElements/#XmlElement combo:
#XmlElements({
#XmlElement(name="code", type=String.class),
#XmlElement(name="percentage", type=Integer.class)
})
public List<Serializable> items;
Alternatively you can also use #XmlElementRef/#XmlElementRef and have a List<JAXBElement<? extends Serializable>> items. Each item would be then a JAXBElement<? extends Serializable> carrying the value as well as name of the element.
But since you seem to have different types (String/Integer), #XmlElements/#XmlElement should work as well and is much easier to use.

Related

JAXB with inheritance but without using child element names

I am working with XML from a vendor system that I cannot change and I am trying to use JAXB with it. The XML is used in a REST like API. See below for a couple message examples. Each request/message has the same root element name that is called MsgRequest and it contains a couple common elements that are in every request but it also contains elements that are dependent on the message type.
<!-- request 1 -->
<MsgRequest>
<SubType>GetUser</SubType>
<RequestID>1</RequestID>
<UserName>joe</UserName>
</MsgRequest>
<!-- request 2 -->
<MsgRequest>
<SubType>GetCompany</SubType>
<RequestID>2</RequestID>
<CompanyName>joe</CompanyName>
</MsgRequest>
From Java perspective I could model it like this using inheritance.
class BaseMessage {
String subType;
Integer requestID;
};
class GetUserMessage {
String userName;
};
class GetCompanyMessage {
String companyName;
};
I don't understand how I would use JAXB to represent this XML. I have done some research and found XMLElementRef but that will marshal the name of the referenced element which is not what I want.
Hopefully I am missing something obvious. Any suggestions appreciated.
XML wasn't designed to fit any particular language or even paradigm. This means thatbut you can design an XML that can be represented by a Java class hierarchy but there's no guarantee that some XML structure that comes your way can be mapped to a class hierarchy. Consider that an XML parser must be able to determine the (complex) type of an XML element from the element's tag name. So, if you have two elements tagged <MsgRequest>, they'll have to be mapped to a single Java type.
You can write an XML Schema for a single complex type with required fields subType and requestID, and optional (minOccurs=0) fields userName and companyName. It won't be good OO style, but it'll generate your XML.

Best practice: Java/XML serialization: how to determine to what class to deserialize?

I have an application that saves its context to XML. In this application, there is a hierarchy of classes, that all implement a common interface, and that represent different settings. For instance, a first setting class may be made of 4 public float fields, another one can be made of a sole HashMap.
I am trying to determine what is the best way to handle writing and reading to XML in a generic way. I read on this site a lot about JAXB and XStream for instance, which are able to make a specific class instance from XML.
However my question is related to the fact that the actual class can be anything that implement a given interface. When you read the XML file, how would you guess the actual class to instantiate from the XML data? How do you do that in your applications?
I thought that I could write the .class name in a XML attribute, read it and compare it to all possible class .class names, until I find a match. Is there a more sensible way?
Thanks
xstream should already take care of this and create the object of correct type.
The tutorial seems to confirm that:
To reconstruct an object, purely from the XML:
Person newJoe = (Person)xstream.fromXML(xml);
If you don't know the type, you will have to first assign it to the common interface type:
CommonInterface newObject = (CommonInterface)xstream.fromXML(xml);
// now you can either check its type or call virtual methods
In my case I just have a kind of header that stores the class name that is serialized and when de-serializing it I just use the header value to figure out to which class shall I de-serialize the values.
A best practice would to use an established, well documented XML parser/mapper. All of the serialization/deserialization work has been done, so you can worry about your business logic instead. Castor and Apache Axiom are two APIs that I have used to marshal/unmarshall(serialize/deserialize) Java Classes and XML.
http://www.castor.org
Apache Axiom

using xsd:any for extensible schema

Until now, I've been handling extensions by defining a placeholder element that has "name" and "value" attributes as shown in the below example
<root>
<typed-content>
...
</typed-content>
<extension name="var1" value="val1"/>
<extension name="var2" value="val2"/>
....
</root>
I am now planning to switch to using xsd:any. I'd appreciate if you can help me choose th best approach
What is the value add of xsd:any over my previous approach if I specify processContents="strict"
Can a EAI/ESB tool/library execute XPATH expressions against the arbitrary elements I return
I see various binding tools treating this separately while generating the binding code. Is this this the same case if I include a namespace="http://mynamespace" and provide the schema for the "http://mynamespace" during code gen time?
Is this WS-I compliant?
Are there any gotchas that I am missing?
Thank you
Using <xsd:any processContents="strict"> gives people the ability to add extensions to their XML instance documents without changing the original schema. This is the critical benefit it gives you.
Yes. tools than manipulate the instances don't care what the schema looks like, it's the instance documents they look at. To them, it doesn't really matter if you use <xsd:any> or not.
Binding tools generally don't handle <xsd:any> very elegantly. This is understandable, since they have no information about what it could contain, so they'll usually give you an untyped placeholder. It's up the the application code to handle that at runtime. JAXB is particular (the RI, at least) makes a bit of a fist of it, but it's workable.
Yes. It's perfectly good XML Schema practice, and all valid XML Schema are supported by WS-I
<xsd:any> makes life a bit harder on the programmer, due to the untyped nature of the bindings, but if you need to support arbitrary extension points, this is the way to do it. However, if your extensions are well-defined, and do not change, then it may not be worth the irritation factor.
Regarding point 3
Binding tools generally don't handle
very elegantly. This is
understandable, since they have no
information about what it could
contain, so they'll usually give you
an untyped placeholder. It's up the
the application code to handle that at
runtime. JAXB is particular (the RI,
at least) makes a bit of a fist of it,
but it's workable.
This corresponds to the #XmlAnyElement annotation in JAXB. The behaviour is as follows:
#XmlAnyElement - Keep All as DOM Nodes
If you annotate a property with this annotation the corresponding portion of the XML document will be kept as DOM nodes.
#XMLAnyElement(lax=true) - Convert Known Elements to Domain Objects
By setting lax=true, if JAXB has a root type corresponding to that QName then it will convert that chunk to a domain object.
http://bdoughan.blogspot.com/2010/08/using-xmlanyelement-to-build-generic.html

Serializing java objects with respect to xml schema loaded at runtime

I call an XML document three-layered if its structure is laid out as following: the root element contains some container elements (I'll call them entities), each of them has some simpleType elements inside (I'll call them properties).
Something like that:
<data>
<spaceship>
<number>1024</number>
<name>KTHX</name>
</spaceship>
<spaceship>
<number>1624</number>
<name>LEXX</name>
</spaceship>
<knife>
<length>10</length>
</knife>
</data>
where spaceship is an entity, and number is a property.
My problem is stated below:
Given
schema: an arbitrary xsd file describing a three-layered document, loaded at runtime.
xmlDocument: an xml document conforming to the schema.
Create
A Map<String, Map <String, Object>> containing data from the xmlDocument, where first key corresponds to entity, second key correponds to this entity's property, and the value corresponds to this property's value, after casting it to a proper java type (for example, if the schema sets the property value to be xs:int, then it should be cast to Integer).
What is the easiest way to achieve this result with existing libraries?
P. S.
JAXB is not really an option here. The schema might be arbitrary and unknown at compile-time. Also I wish to avoid an excessive use of reflection (associated with converting the beans to maps). I'm looking for something that would allow me to make the typecasts while xml is being parsed.
I think XMLBeans is worth a shot; saved the day more than once...
Basically you run an ant script to generate the beans handling the schema, then you iterate over the nodes to build your map.
I think JAXB (tutorial here) is the easiest way to do this.
Create your XML structure like:
#XmlRootElement
class data {
public List<SpaceShipType> spaceship;
public KnifeType knife;
}
class SpaceShipType {
public int number;
public String name;
}
class KnifeType {
public int length;
}
Then create the object tree, and use JAXB.marshall() to write the XML.
If you have an existing XSD, use the xjc tool to create the classes for you.
You will need reflections if the schema is unknown at compile time.
I suggest to take a look at some other tools like xstream.
I could recommend the simple framework or you could do parsing/wrinting xml with pure dom4j as I did in the timefinder project. I used the 'pure' approach because I had cycles in my object graph, but I wanted that the xml could be validated with an xml schema (and I did not want to use JAXB)
I have finally went with using Castor library for parsing the schema and assigning data types manually.

JAXB - Add a node to the XML as html link

I have a basic JavaBean in my service layer and originally it was not designed for marshalling. This object is used by both JAX-WS, JAX-RS, and Servlets in other layers of my application. I want to take advantage of a drill down type effect in my REST services so my question is: How do I make one of the fields/properties of the javabean appear in the xml as an HTML Link? Obviously I need to use CData. I cannot modify the original javabean by adding fields, etc. Is there an annotation I can use?
If I have in my class:
...
String data;
...
how do I make that(in xml):
<data><![CDATA[ValueOfData]]></data>
is this possible with JAXB and Annotations? Maybe xlink?
I suggest using a type adapter. These are normally used to adapt XML string values into more strongly-typed values like timestamps, but you can also use them to adapt strings to strings.
First, create a subclass of javax.xml.bind.annotation.adapters.XmlAdapter. This class will have to implement marshal and unmarshal, converting to and from the value of your field, and the HTML fragment in the XML.
Once you have that, you can annotate your field with
#XmlJavaTypeAdapter(MyAdapter.class)
And that should be it.

Categories