How to manage several xml format with jaxb/moxy - java

I need to perform some calls to a backend not managed by me. The point is that they use different xml format to trasport the data. Even for the same call request and response are different: same data but different root tags.
At first I tried to map them with different beans using annotations (moxy jaxb), but the I had to manage to many of them and rhe cost of change was too high. Then I switched to xml file mapping: it seems better then previous choice, but still I have to manage lots of xml files (douzen).
Have you got any proposal in order to keep the project light and scalable?
Thanks in advance for the help.

One of the reasons we added the external mapping document in EclipseLink JAXB (MOXy) is to allow you to apply additional XML representations to your object model:
http://blog.bdoughan.com/2011/09/mapping-objects-to-multiple-xml-schemas.html
One thing to pay attention to is the xml-mapping-metadata-complete attribute on the root xml-bindings element. When this is set to true the XML document replaces all other mappings, and when it is false or not present it amends the metadata.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="blog.weather"
xml-mapping-metadata-complete="true">
Using the amend the metadata approach you can pass in multiple binding files to create the Context where subsequent mapping files can be used to progressively tweak the metadata.
Map<String, Object> properties = new HashMap<String, Object>(1);
List<String> bindingFiles = new ArrayList<String>(2);
bindingFiles.add("version2.xml");
bindingFiles.add("version3.xml");
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, bindingFiles);
JAXBContext jc = JAXBContext.newInstance(new Class[] {WeatherReport.class}, properties);

Related

jaxb java class to multiple xml mappings

I have a couple of java classes that I want to convert to xml using jaxb. (I have no need to generate the classes based on the schema) I need to be able to map the class to different xml formats so I do not want to use annotations. From what I've seen my best option seems to be to use external xml bindings. So I wanted to know:
1) I am using eclipse. I am new to JAXB and I would like to know how to integrate external bindings using eclipse?
2) What other options other than external xml bindings are available?
I think you best option is to use MOXy XML bindings:
http://www.eclipse.org/eclipselink/documentation/2.6/moxy/runtime003.htm
This allows you to define XML<->Java mappings in form of XML files instead of annotations:
<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="example">
<java-types>
<java-type name="Customer">
<xml-root-element name="customer-info" />
<java-attributes>
<xml-attribute java-attribute="custId" name="customer-id" />
<xml-element java-attribute="picture" name="picture-hex">
<xml-schema-type name="hexBinary" />
<xml-java-type-adapter
value="example.adapters.MyHexConverter" />
</xml-element>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
You can use this file via JAXBContextProperties.OXM_METADATA_SOURCE property:
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, iStream);
JAXBContext ctx = JAXBContext.newInstance(new Class[] { Customer.class }, properties);
So if you want several different mappings for the same class, just write several XML bindings and create your JAXBContext with corresponding files. I think this is the best options right now, with MOXy
With pure JAXB RI you can write an own annotations reader. I did this once with Annox:
http://confluence.highsource.org/display/ANX/JAXB+User+Guide
Another option was JBoss JAXBIntroductions, also based on a custom annotations reader:
https://developer.jboss.org/wiki/JAXBIntroductions
But I'm not sure this is live anymore.
Since you want multiple mappings, you'll have to write them (all but one) manually. You can generate one set of mappings as annotations, but further mappings will have to be written manually. Or, let's say, I'm not aware of a plugin or tool which would generate, for instance, MOXy XML bindings. Wouldn't be a big problem to write one though.
You may also take a completely different approach. Instead of mapping one central model with different mappings/format, you can have map a separate package of DTOs onto these formats. And then convert between your DTOs and the central model. Something like
XML(1) <-> DTO(1)|<-\
XML(2) <-> DTO(2)|<--*->Model
XML(3) <-> DTO(3)|<-/
Thus you'll have clean DTOs per exchange format (which you can generate out of schemas) and a single central business model (to rule them all). You'll have to convert between DTOs and the Model, this can be handled with a tool like Dozer.
If this is a better approach or not depends on how complex your formats are and how different they are fron one another.
Concerning your questions:
1) There's nothing special about Eclipse, just add MOXy as dependency and follow the docs.
2) I've described a few options above.
See "Passing Customization Files to the JAXB Binding Compiler" here:
https://docs.oracle.com/javase/tutorial/jaxb/intro/custom.html
You could write a script to integrate into Eclipse. Or you could use a Maven or Ant task.
Your only options are annotations or XML bindings.

how to get query result as XML in hibernate?

I am using struts2 with hibernate. Does anyone know if it is possible to return query result as XML instead of ArrayList of domain objects?
Hibernate by default maps and persists a database record thought POJO , but in fact it also supports persisting , mapping and representing a database record in XML by using an experimental features called Dynamic models.
For example , to output a record in XML:
/**Get the a new session that is in the DOM4J EntityMode**/
Session dom4jSession = session.getSession(EntityMode.DOM4J);
Element outputXML=(Element) dom4jSession.get(Employee.class, employeeId);
XMLWriter writer = new XMLWriter( System.out, OutputFormat.createPrettyPrint() );
writer.write( outputXML);
To configure the format of the outputted XML , you can only do it by mapping the entity in XML . AFAIK ,there are no annotation equivalent .
Hibernate is an Object-Relational Mapper, meaning it maps a Relational database to objects. You want to use Hibernate to return an object and then use an XML Serializer to convert to XML.
The Simple Serializer is probably the best one to get started with. The Website contains a lot of tutorials and examples.
http://simple.sourceforge.net/
However there are a ton of XML Serializers for Java:
http://karussell.wordpress.com/2009/09/03/xml-serializers-for-java/
Maybe you could, once you have the result use XStream to parse the entire result to XML. A simple tutorial on XStream is available here.

Spring JAXB - Unmarshalling an XML document with schema validation

I am trying to work out how to unmarshall and XML document to a Java document.
The top of the xml document looks like this
<xs:myData xmlns:xs="http://www.example.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.example.com example.xsd ">
There is a schema file whose top section looks like this:
<schema targetNamespace="http://www.example.com"
elementFormDefault="qualified"
xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:xs="http://www.example.com">
I would like to unmarshall the xml document using Spring/JaxB and eventually convert it to a JPA object. I am not sure how to go about so i looked for examples on google and came up with this http://thoughtforge.net/610/marshalling-xml-with-spring-ws-and-jaxb/
I understand most of it except how or where the schema is used.
I have seen other examples where the schema is explicitly specified, i.e.
SchemaFactory schemaFac = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema sysConfigSchema = schemaFac.newSchema(
new File("example.xsd"));
unmarshaller.setSchema(sysConfigSchema);
RootElement root = (RootElement)unmarshaller.unmarshal(
new File("example1.xml"));
How is the schema shown in the first link used to validate the xml document?
Are there any disadvantages to using Spring's jaxb2Marshaller as opposed to direct use of JAXB?
What is the effect of having the namespace next to the XmlElement annotation? (See the Person class)
I would appreciate any more examples showing Spring/REST with unmarshalling with schema validation.
Thanks
As far as I know JAXB does not parse xsi attribute to dereference XSD, load it and use for validation. Perhaps that was done to disable automatic validation, otherwise it would be problematic to switch it off :)
Spring Jaxb2Marshaller was obviously added to implement the same interface org.springframework.oxm.Marshaller (which is implemented also by CastorMarshaller, JibxMarshaller, ...). It is very powerful and allows you to tune JAXBContext in very flexible way (I can't imagine the scenario when provided API is not enough). From pattern point of new Jaxb2Marshaller is a builder, so it does not add anything to core JAXB functionality. But there are some evident advantages. For example, schema loading is very simple. In the article the Spring context refers the person.xsd (<property name="schema" value="classpath:schema/person.xsd"/>) which one need to put into resources explicitly. Then JAXB marshaller/unmarshaller will use this schema to validate XML when XML is generated/loaded.
#XmlElement(..., namepsace="xxx") will automatically generate this XML element with a specified namespace. It's rare case if somebody does not use namespaces. I would say writing XSD without namespaces is not normal, as you want to avoid the element name collision.
Using JAXB with RestTemplate is very simple. You need to be sure that JAXB runtime is in your classpath (JDK 6 already has it) and your bean is annotated with #XmlRootElement. Then just use Person person = restTemplate.getForObject(restServiceUrl, Person.class),

After I got XML data, how to parse it and transfer to JSON?

In Jersey RESTful frame work, I know I can get xml data in client as following:
private static final String BaseURI = "http://DOMAN.com";
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource service = client.resource(BaseURI);
String xmlData = service.path("rest").path("todos").accept(
MediaType.APPLICATION_XML).get(String.class)
My question is how can I parse the xmlData then? I would like to get the needed data from xmlData, and transfer the needed data to JSON, what is the best way to implement this?
As a general rule, NEVER convert straight from XML to JSON (or vice versa) if you do not have to.
Rather, bind data from XML or JSON to POJOs, then do the other conversion. While it may seem non-intuitive this results in cleaner result and less problems, since conversions between POJOs and data formats have much more options, mature, well-designed libs; and POJOs are easier to configure (with annotations) and have more metadata to guide conversion process.
Direct conversions libs (like Jettison, see below) are plagued with various issues; often producing "franken-JSON", JSON that is technically correct but looks alien because of added constructs needed by conversion.
In case of Jersey, then, use JAXB for XML to/from POJOs, and Jackson for doing the same with JSON. These are libraries Jersey uses anyway; and direct usage is quite easy.
If you absolutely insist on direct conversion, you could try Jettison, but be prepared to hit a problem with Lists, arrays and Maps, if you need them (esp. single-element arrays -- arrays are problematic with XML, and auto-conversion often goes wrong).
If your service doesn't provide JSON as an option already (what happens if you change MediaType.APPLICATION_XML to MediaType.APPLICATION_JSON?), then I believe you have a few options, which I list in order of my preference.
Option 1: You have an XML schema for the the data
If you have an XML schema for the returned XML, you could use xjc to generate the JAXB annotated java classes and then leverage jackson to convert the instances to JSON data. I think this will get you going fast by leveraging this libraries over doing the parsing youself. Jackson is a robust library, used by glassfish for their Jersey(JAX-RS) implementation and I don't feel there is any risk in depending on this library.
Option 2: Use the json.org library, but I've had significant problem with this library having to do with its reflection-based methodology, etc. That said, it might work well for you...and you can test relatively easily and see if it does meet your requirements. If so...you're done! =)
Option 3: You don't have the XML schema and/or you want more control
as #Falcon pointed out, you can always use traditional XML parsing technologies to parse the XML into whatever you want. I'm partial to SAX parsing, but DOM could work depending on xml side
Regards,
Steve
Simplest and easiest way would be using org.json package : http://json.org/javadoc/org/json/XML.html
XML.toJSONObject(xmlData).toString()
Just this one line apart from necessary import statement will do it all.
Now that i have mentioned org.json library, lot of people may comment bad about it. Remember, I have said the simplest and easiest way, not the best or the most performant way ;-)
In case you are using maven, add this dependency :
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
Do you have any access to the "lower level interface" that generates the XML? If you do, the only change needed is to have the xml objects annotated with "#XmlRootElement". Then, you can just pass back the XMLobject as JSON without any further code.
Check Jsonix. If you have an XML schema, you can generate XML-JSON mappings and unmarshal/marshal XML in JavaScript. Very similar to JAXB (which Steve Siebert mentioned), but works on client.
// The PO variable provides Jsonix mappings for the purchase order test case
// Its definition will be shown in the next section
var PO = { };
// ... Declaration of Jsonix mappings for the purchase order schema ...
// First we construct a Jsonix context - a factory for unmarshaller (parser)
// and marshaller (serializer)
var context = new Jsonix.Context([ PO ]);
// Then we create an unmarshaller
var unmarshaller = context.createUnmarshaller();
// Unmarshal an object from the XML retrieved from the URL
unmarshaller.unmarshalURL('/org/hisrc/jsonix/samples/po/test/po-0.xml',
// This callback function will be provided with the result
// of the unmarshalling
function(result) {
// We just check that we get the values we expect
assertEquals('Alice Smith', result.value.shipTo.name);
assertEquals('Baby Monitor', result.value.item[1].productName);
});

Add Xml namespace using Jaxb

I am creating amazon feed, the feed xml should be like:
<AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="amzn-envelope.xsd">
I am using jaxb to generate xml files from java classes, I used NamespacePrefixMapperImpl from jaxb samples to add namespaces to the xml. But currently it generates the root like:
<AmazonEnvelope xmlns:xsi:noNamespaceSchemaLocation="amzn-envelope.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
how I can remove the xmlns from amzn-envelope.xsd??
Here's the mapping I used in NamespacePrefixMapperImpl
if( "http://www.w3.org/2001/XMLSchema-instance".equals(namespaceUri) )
return "xsi";
if("amzn-envelope.xsd".equals(namespaceUri))
return "xsi:noNamespaceSchemaLocation";
I found a property at Marshaller that can add the amzn-envelope.xsd:
marshaller.setProperty("jaxb.noNamespaceSchemaLocation", "amzn-envelope.xsd");
and left the NamespacePrefixMapper to add the "http://www.w3.org/2001/XMLSchema-instance".
Hope this helps others.
If I understand your intent, your document has a default namespace, and you're trying to add the schemalLocation for that namespace.
NamespacePrefixMapper won't let you do this, it's useful only for picking a prefix for a namespace. There are no namespaces in this document, and so no useful way of using NamespacePrefixMapper. It can't be used for adding schemaLocation hints - those are treated specially by JAXB, and you're just confusing it.
Are you sure you need the noNamespaceSchemaLocation="amzn-envelope.xsd" at all? Have you tried sending it to the web service without it?

Categories

Resources