Java: Populating .xsd-generated class from .xml file - java

I have a class that was generated from an .xsd file, and I have an .xml file that contains data that adheres to the schema in the .xsd. Something like:
XML schema file: MyObject.xsd
Java class generated from schema: MyObject.java
XML that matches the schema: MyObject.xml
Is there an easy way for me to deserialize MyObject.xml into an instance of MyObject.java? I'm hoping for something easier than hand-walking through the DOM elements and setting all the properties on the object.
Basically, I'm looking for the functionality in java.beans.XMLDecoder, but since my .xml file was not created from the XMLEncoder, I do not believe that I can use the decoder.

First you need to create a JAXBContext instance using the static newInstance method. Then create an Unmarshaller instance using the createMarshaller method and call the appropriate unmarshal method on that instance:
InputStream is = new FileInputStream("MyObject.xml");
JAXBContext jc = JAXBContext.newInstance(MyObject.class);
Unmarshaller u = jc.createUnmarshaller();
MyObject o = (MyObject)u.unmarshal(is);

Related

How to use XSD that includes another XSD with JAXB while XSDs reside in JAR?

I am reading XML files that are validated via XSD files into XJC generated classes. That all works fine when I reference the XSD in the normal file system. Now I want to bundle the XSD into my JAR. That also works fine as long the XSD is standalone with the following code:
//Use the schema factory to get the schema
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
//Get XSD from JAR
InputStream schemaStream = getClass().getResourceAsStream("/schema/myschema.xsd");
Schema schema = sf.newSchema(new StreamSource(schemaStream));
//parse the XML file and fill the data model
Class<T> c = getXmlDataModelClass();
JAXBContext jaxbContext = JAXBContext.newInstance(c);
m_JaxbUnmarshaller = jaxbContext.createUnmarshaller();
//set the schema to be considered
m_JaxbUnmarshaller.setSchema(schema);
return (T)m_JaxbUnmarshaller.unmarshal(file);
Now the problem: if myschema.xsd includes another XSD:
<xs:include schemaLocation="BaseTypes.xsd"/>
The types in the included XSD are not found.
I also tried to pass an array of two StreamSource with both XSDs into sf.newSchema(), but that did not help.
The easiest is to use URLs, something like (not tested):
URL schemaURL = getClass().getResource("/schema/myschema.xsd");
Schema schema = sf.newSchema(schemaURL);
You'll get a jar:... URL and feed it to the schema factory. As long as included schemas reside in the same JAR, they should be resolved without problems.
For more advanced usage you may instantiate and provide a resource resolver to the schema factory:
sf.setResourceResolver(myResourceResolver);
The resource resolver resolves schemas into resources. You can use something like XMLCatalogResolver to rewrite schema URLs using catalog files, for instance. This would allow you to rewrite absolute URLs into local resources.

JAXB is confused about root elements?

I'm trying to marshal a file using the Visio XML Schema, which consists of 3 schema files and produces three packages when java source is generated with XJC:
com.microsoft.schemas.visio._2003.core
com.microsoft.schemas.visio._2006.extension
com.microsoft.schemas.office.visio._2010.extension
The root element is VisioDocument, and all of the classes I'm using are in the 2003 package.
Here is my approach to marshalling my XML file:
VisioDocumentType visioDoc = new VisioDocumentType();
... manipulated here ...
JAXBContext jc = JAXBContext.newInstance("com.microsoft.schemas.visio._2003.core");
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.marshal(new JAXBElement<VisioDocumentType>(new QName("uri","local"), VisioDocumentType.class, visioDoc), bw);
When executed, I receive this error:
javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "com.microsoft.schemas.visio._2003.core.PagePropsType" as an element because it is missing an #XmlRootElement annotation]
I am using PagePropsType, but it is not a root element. Why does JAXB think it is?
The problem resides in the ... manipulated here ... part of your code.
Based on the assumption that you do the following (or something similar).
// you create a page prop
PagePropsType pageProps = ...
// then you feed it to a shape sheet
ShapeSheetType shapeSheet = ...
shapeSheet.getTextOrXFormOrLine().add(pageProps);
(ShapeSheetType is a superclass for StyleSheetType, et cetera.)
If this's the case, then your problem lies in adding the pageProps to the list directly.
If you take a look at the getTextOrXFormOrLine() method's documentation it lists what kind of types the list can hold. Every type is wrapped in a JAXBElement<...> so you have to wrap pageProps before adding it to the list.
You should do it like this:
ObjectFactory objectFactory = new ObjectFactory();
JAXBElement<PagePropsType> pagePropsElement = objectFactory.createShapeSheetTypePageProps(pageProps);
(Note that I've used XJC 2.2.4 to compile the schemas; for me every class' name is suffixed with Type. Maybe this is why I ended up with VisioDocumentType instead of VisioDocument like you, but it shouldn't matter.)
If you check your generated code, you will find a ObjectFactory class in there. This class should have a method that returns a VisioDocument wrapped in a JAXBElement, and that it the object that you want to pass to the marshaller.
Same applicable to all objects you set inside VisioDocument - don't create them with 'new' but use ObjectFactory.

Printing a JAXB generated bean

I have a JAXB data class which is generated from wsimport and I'd like to print it to the console and/or log. Unfortunately a toString is not generated.
What's the easiest way to print the data object? It doesn't matter whether the output is the original XML or something else, as long as it's readable.
It looks like the class is a valid bean (properly named getters and setters) so anything that works with beans is probably fine too.
For printing to the console, try this:
jaxbContext.createMarshaller().marshal(jaxbObject, System.out);
To get it into a String, use a StringWriter:
StringWriter writer = new StringWriter();
jaxbContext.createMarshaller().marshal(jaxbObject, writer);
String xmlString = writer.toString();
To get the JAXBContext object you need to do the following:
JAXBContext jaxbContext = JAXBContext.newInstance(<WhateverClass>.class);
Where <WhateverClass> is the class literal for the type that jaxbObject is. You should also be able to do:
JAXBContext jaxbContext = JAXBContext.newInstance(jaxbObject.getClass());
Depending on where you are defining the context and your stylistic preference. JAXBContext is thread-safe so it is good to define one instance and share it. Marshaller and Unmarshaller make no such guarantees though. So they need to be created on demand.

Can JAXB store the class name in the XML so that the the deserialize code doesn't need knowledge of the class?

It seems the standard approach for deserializing JAXB XML is to specify the package name when creating the context. Then, JAXB looks up the class based on the root element:
JAXBContext jc = JAXBContext.newInstance("com.foo");
Unmarshaller u = jc.createUnmarshaller();
Object o = u.unmarshal(new StringReader("<?xml version="1.0" encoding="UTF-8" standalone="yes"?><MyJaxb>..."));
I'm looking for a more flexible approach where I don't have to specify the package name and could still deserialize any object. This would be as simple as JAXB storing the package in the XML, but I can't seem to find out how to do this. I can write the code to do it myself but that would be unpleasant. It would like JAXB to do it, if possible. BTW, I am not using schemas, just Annotations and marshal/unmarshal. Any ideas?
Actually you can not deserialize "any" object with pure JAXB. You have to specify either packages (where ObjectFactory.class will be sought) or list of classes like JAXBContext.newInstance(Class1.class, Class2.class, Class3.class); That's how jaxb works, it's a part of agreement.
If your tasks are wider that that, e.g. building java classes from arbitrary xml data structure - it's also possible, but you have to be a bit more concrete - what do you mean under "more flexible approach".
You should be able to add more than one package when you get the instance of the jaxbcontext object. You can add as many packages as you want like below.
JAXBContext jc = JAXBContext.newInstance("com.foo.package1:com.foo.package2" );
however, I am not sure how you are gonna use it if you deserialize it into an Object instance?
Are you not gonna use what you have just deserialized?
Also Unmarshaller is not a thread safe class if your application is a multithreaded one.

JAXB to unmarshall <string>foobar</string>

Greetings! I have a server returning XML content to my client that looks like this:
<string xmlns="...">foobar</string>
I'm new to JAXB and have gotten a lot of stuff working, except this. I thought it would be pretty easy to marshal and unmarshal this to and from a String. It took a while, but I finally figured out how to marshal this as
public static String ToXML(String s) throws Exception
{
JAXBContext context = JAXBContext.newInstance(String.class);
Marshaller marshaller = context.createMarshaller();
StringWriter sw = new StringWriter();
marshaller.marshal(new JAXBElement(new QName("", "String"), String.class, s), sw);
return sw.toString();
}
So my question is, how to I unmarshal this? It cannot be annotated as a root element. I cannot use java.lang as the package to create a new instance of a JAXBContext (I get an ObjectFactory missing exception).
Any wisdom to impart? This can't be that hard, right?
You need to write an object model that conforms to your XML structure, and tell JAXB to unmarshal on to that. Your example may look simple, but it's not what JAXB is for.
Try something like this:
#XmlRootElement(name="string", namespace="blah")
public class MyString {
#XmlValue
String value;
}
JAXBContext context = JAXBContext.newInstance(MyString.class);
MyString myString = (MyString) context.createUnmarshaller().unmarshal(...);
This will unmarshal the XML <string xmlns="blah">foobar</string>. Change the namespace accordingly. If you have many namespaces, then JAXB isn't really the tool for you.
I was surprised by this, but the following seems to work:
final String xmlString = "<string>value</string>";
final StringReader xmlReader = new StringReader(xmlString);
final StreamSource xmlSource = new StreamSource(xmlReader);
final JAXBContext jaxbContext = JAXBContext.newInstance(String.class);
final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
final String stringValue = unmarshaller.unmarshal(xmlSource, String.class).getValue();
Is that what you were looking for?
When you're using JAXB, you need to build the code around an XML schema. That is, if you have a file, say foo.xsd, you need to run it through the xjc compiler (in JDK 6 by default, otherwise you can download JAXB 2 and use that). That will read through the schema and generate the Java bean and associated ObjectFactory classes with the elements in the schema. The Java bean classes will look like regular POJOs with annotations. The ObjectFactory classes are needed by the JAXB implementation to convert the XML into the corresponding Java bean. This explains your ObjectFactory missing exception.
So it's not hard, but there is some leg work involved. We use it for one of our production applications and it's great. I see myself using it more now that it's part of the JDK.

Categories