JAXB marshals XML differently to OutputStream vs. StringWriter - java

I apologize if this has been answered, but the search terms I have been using (i.e. JAXB #XmlAttribute condensed or JAXB XML marshal to String different results) aren't coming up with anything.
I am using JAXB to un/marshal objects annotated with #XmlElement and #XmlAttribute annotations. I have a formatter class which provides two methods -- one wraps the marshal method and accepts the object to marshal and an OutputStream, the other just accepts the object and returns the XML output as a String. Unfortunately, these methods do not provide the same output for the same objects. When marshaling to a file, simple object fields internally marked with #XmlAttribute are printed as:
<element value="VALUE"></element>
while when marshaling to a String, they are:
<element value="VALUE"/>
I would prefer the second format for both cases, but I am curious as to how to control the difference, and would settle for them being the same regardless. I even created one static marshaller that both methods use to eliminate different instance values. The formatting code follows:
/** Marker interface for classes which are listed in jaxb.index */
public interface Marshalable {}
/** Local exception class */
public class XMLMarshalException extends BaseException {}
/** Class which un/marshals objects to XML */
public class XmlFormatter {
private static Marshaller marshaller = null;
private static Unmarshaller unmarshaller = null;
static {
try {
JAXBContext context = JAXBContext.newInstance("path.to.package");
marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
unmarshaller = context.createUnmarshaller();
} catch (JAXBException e) {
throw new RuntimeException("There was a problem creating a JAXBContext object for formatting the object to XML.");
}
}
public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException {
try {
marshaller.marshal(obj, os);
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
public String marshalToString(Marshalable obj) throws XMLMarshalException {
try {
StringWriter sw = new StringWriter();
return marshaller.marshal(obj, sw);
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
}
/** Example data */
#XmlType
#XmlAccessorType(XmlAccessType.FIELD)
public class Data {
#XmlAttribute(name = value)
private String internalString;
}
/** Example POJO */
#XmlType
#XmlRootElement(namespace = "project/schema")
#XmlAccessorType(XmlAccessType.FIELD)
public class Container implements Marshalable {
#XmlElement(required = false, nillable = true)
private int number;
#XmlElement(required = false, nillable = true)
private String word;
#XmlElement(required = false, nillable = true)
private Data data;
}
The result of calling marshal(container, new FileOutputStream("output.xml")) and marshalToString(container) are as follows:
Output to file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="project/schema">
<number>1</number>
<word>stackoverflow</word>
<data value="This is internal"></data>
</ns2:container>
and
Output to String
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:container xmlns:ns2="project/schema">
<number>1</number>
<word>stackoverflow</word>
<data value="This is internal"/>
</ns2:container>

Looks like this might be a "bug" in JAXB. Looking at the source, the calls for marshal() create different writers based on the output/writer type parameter:
public void marshal(Object obj, OutputStream out, NamespaceContext inscopeNamespace) throws JAXBException {
write(obj, createWriter(out), new StAXPostInitAction(inscopeNamespace,serializer));
}
public void marshal(Object obj, XMLStreamWriter writer) throws JAXBException {
write(obj, XMLStreamWriterOutput.create(writer,context), new StAXPostInitAction(writer,serializer));
}
The implementations of the writers is different with regards to how they handle "empty elements". The above code is from:
jaxb-ri\runtime\src\com\sun\xml\bind\v2\runtime\MarshallerImpl.java.
The two writers you are creating are:
jaxb-ri\runtime\src\com\sun\xml\bind\v2\runtime\output\UTF8XmlOutput.java
jaxb-ri\runtime\src\com\sun\xml\bind\v2\runtime\output\XMLStreamWriterOutput.java

The good news is that JAXB is a specification with more than one implementation (just like JPA). If one implementation is not meeting your needs, others are available such as EclipseLink JAXB (MOXy):
http://www.eclipse.org/eclipselink/moxy.php

I don't know why JAXB is doing this - or even if it is JAXB - if JAXB is outputting XML via a SAXContentHandler for example, then it has no direct control over how close tags are produced.
To get consistent behaviour, you could wrap your OutputStream in a OutputStreamWriter, e.g.
public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException {
try {
marshaller.marshal(obj, new OutputStreamWriter(os, "UTF-8"));
} catch (JAXBException jaxbe) {
throw new XMLMarshalException(jaxbe);
}
}
Along the same lines, you might see what happens if you wrap the StringWriter in a PrintWriter. Maybe there is some custom code that detects StringWriter to tries to keep the output short as possible. Sounds unlikely, but I have no other explanation.

Related

Parsing wrapped attributes with jaxb

Given something like:
<root>
<wrapper>
<wrapped id="..."/>
<wrapped id="..."/>
</wrapper>
</root>
how can I map it to this POJO:
public class Root {
public Set<UUID> myIds = new LinkedHashSet();
}
I am wondering since #XmlElement( name = "wrapped" ) #XmlElementWrapper( name = "wrapper" ) works somewhat similar to what I want, is there something to get the attribute?
note: i am not using moxy so as far as I know, I cannot use xpaths. I am trying to avoid the #XmlJavaTypeAdapter route.
You need to modify your root class a little bit,
so that it will contain a Set<Wrapped> instead of a Set<UUID>.
#XmlRootElement(name = "root")
public class Root {
#XmlElementWrapper(name = "wrapper")
#XmlElement(name = "wrapped")
public Set<Wrapped> myWrappeds = new LinkedHashSet<>();
}
And you need a separate class for the <wrapped> elements.
Surprisingly for me, you don't need an #XmlJavaAdapter for id here, because JAXB already has a built-in converter between java.util.UUID and String.
public class Wrapped {
#XmlAttribute(name = "id")
public UUID id;
}
I have checked the above with this XML input file
<root>
<wrapper>
<wrapped id="550e8400-e29b-11d4-a716-446655440000"/>
<wrapped id="550e8400-e29b-11d4-a716-446655440001"/>
</wrapper>
</root>
and this main method which reproduces the original XML:
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File file = new File("root.xml");
Root root = (Root) unmarshaller.unmarshal(file);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}

any way to convert xml to object in java?

I am trying to use a XML and access to all fields and data on an easy way, so, I decided to use JaxB , but I have no idea how to create all the classes for the objects, I tried manually like this.
#XmlRootElement(name = "Response")
public class Response {
#XmlElement(ns = "SignatureValue")
String signatureValue;
}
But I get an error on #XmlElement saying the annotation is disallowed for this location...
I checked other posts and they work great if I have something like Hellw but doesnt work with more complex formats, an example of first part of mine is like this
<?xml version="1.0" encoding="UTF-8"?><DTE xsi:noNamespaceSchemaLocation="http://www.myurl/.xsd" xmlns:gs1="urn:ean.ucc:pay:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
any idea how to do all this??
Thanks in advance
EDIT:
I forgot to say, the XML is actually a String with the entire XML.
The #XmlElement annotation is valid on a field. If you have a corresponding property then you should annotate the class with #XmlAccessorType(XmlAccessType.FIELD) to avoid a duplicate mapping exception.
Java Model
Annotating the Field
#XmlRootElement(name = "Response")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response {
#XmlElement(name = "SignatureValue")
String signatureValue;
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
Annotating the Property
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Response")
public class Response {
String signatureValue;
#XmlElement(name = "SignatureValue")
public String getSignatureValue() {
return signatureValue;
}
public void setSignatureValue(String signatureValue) {
this.signatureValue = signatureValue;
}
}
For More Information
http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html
Demo Code
Below is some demo code that reads/writes the XML corresponding to your Response class.
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum19713886/input.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Response>
<SignatureValue>Hello World</SignatureValue>
</Response>

how do I convert an xml string to an object. I don't know what object ahead of time. Just several possibities

I have a string of that is an XML string and it could correspond to one of several objects that are jaxb generated schema files.
I don't know what object it is ahead of time.
How do convert this XML string to an jaxb xml object? Some type of unmarshalling?
How do I determine which object it is assigned to?
How do I instantiate the object once it is converted from xml string to the object?
You could do something like the following:
Foo
As long as there is a root element associated with your class via an #XmlRootElement or #XmlElementDecl annotation you don't need to specify the type of class that you are unmarshalling (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Foo {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
Demo
To unmarshal from a String simply wrap the String in an instance of StringReader. The unmarshal operation will convert the XML into an instance of your domain class. If you don't know what class you will have to use instanceof or getClass() to determine what type it is.
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
String xml = "<foo><bar>Hello World</bar></foo>";
StringReader reader = new StringReader(xml);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Object result = unmarshaller.unmarshal(reader);
if(result instanceof Foo) {
Foo foo = (Foo) result;
System.out.println(foo.getBar());
}
}
}
Output
Hello World
Unmarshaller yourunmarshaller = JAXBContext.NewInstance(yourClass).createUnMarshaller();
JAXBElement<YourType> jaxb = (yourunmarshaller).unmarshal(XMLUtils.getStringSource([your object]), [the class of your object].class);
If you have schema files for the XML objects, which you would if you're using JAXB, run a validate on the XML.
Java XML validation against XSD Schema
If you generate objects from XSD, then JAXB generated an ObjectFactory class in the same package as all the type classes.
JAXBContext jaxbContext = JAXBContext.newInstance("your.package.name");
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Here "your.package.name" stands for the package name of your ObjectFactory class.
The unmarshaller can now convert your XML into objects:
public Object createObjectFromString(String messageBody) throws JAXBException {
return unmarshaller.unmarshal(new StringReader(messageBody));
}
If this is successful, a JAXBElement object will be returned:
try {
JAXBElement jaxbElement= (JAXBElement) createObjectFromString(messageBody);
} catch (JAXBException e) {
// unmarshalling was not successful, take care of the return object
}
If you have a jaxbElement object returned, you can call getValue() for the wrapped object, of getDeclaredType() for it's class.
With this method, you don't need to know the type of the target object in advance.

JAXB creating context and marshallers cost

The question is a bit theoretical, what is the cost of creating JAXB context, marshaller and unmarshaller?
I've found that my code could benefit from keeping the same JAXB context and possibly the same marshaller for all marshaling operations rather than creating context and marshaller on each marshaling.
So what is the cost of creating JAXB context and marshaller/unmarshaller? Is it okay to create context+marshaller for each marshaling operation or it's better to avoid it?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
JAXBContext is thread safe and should only be created once and reused to avoid the cost of initializing the metadata multiple times. Marshaller and Unmarshaller are not thread safe, but are lightweight to create and could be created per operation.
Ideally, you should have a singleton JAXBContext and local instances of Marshaller and Unmarshaller.
JAXBContext instances are thread-safe while Marshaller and Unmarshaller instances are not thread-safe and should never be shared across threads.
It's a pity that this isn't specifically described in the javadoc. What I can tell is that Spring uses a global JAXBContext, shared between threads, whereas it creates a new marshaller for each marshalling operation, with a javadoc comment in the code saying that JAXB marshallers are not necessarily thread-safe.
The same is said on this page:https://javaee.github.io/jaxb-v2/doc/user-guide/ch03.html#other-miscellaneous-topics-performance-and-thread-safety.
I would guess that creating a JAXBContext is a costly operation, because it involves scanning classes and packages for annotations. But measuring it is the best way to know.
JAXB 2.2 (JSR-222) has this to say, in section "4.2 JAXBContext":
To avoid the overhead involved in creating a JAXBContext instance, a
JAXB application is encouraged to reuse a JAXBContext instance. An
implementation of abstract class JAXBContext is required to be
thread-safe, thus, multiple threads in an application can share the
same JAXBContext instance.
[..]
JAXBContext class is designed to be immutable and thus threadsafe.
Given the amount of dynamic processing that potentially could take
place when creating a new instance of JAXBContxt, it is recommended
that a JAXBContext instance be shared across threads and reused as
much as possible to improve application performance.
Unfortunately, the specification does not make any claims regarding thread-safety of Unmarshaller and Marshaller. So it is best to assume they are not.
I solved this problem using:
shared thread safe JAXBContext and thread local un/marschallers
(so theoretically, there will be as many un/marshaller instances as there are threads which accessed them)
with synchronization only on un/marshaller's initialization.
public class MyClassConstructor {
private final ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<Unmarshaller>() {
protected synchronized Unmarshaller initialValue() {
try {
return jaxbContext.createUnmarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create unmarshaller");
}
}
};
private final ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<Marshaller>() {
protected synchronized Marshaller initialValue() {
try {
return jaxbContext.createMarshaller();
} catch (JAXBException e) {
throw new IllegalStateException("Unable to create marshaller");
}
}
};
private final JAXBContext jaxbContext;
private MyClassConstructor(){
try {
jaxbContext = JAXBContext.newInstance(Entity.class);
} catch (JAXBException e) {
throw new IllegalStateException("Unable to initialize");
}
}
}
Even better!! Based on the good solution from the post above, create the context just-once in the constructor, and save it instead of the class.
Replace the line:
private Class clazz;
with this one:
private JAXBContext jc;
And the main constructor with this one:
private Jaxb(Class clazz)
{
this.jc = JAXBContext.newInstance(clazz);
}
so in the getMarshaller/getUnmarshaller you can remove this line:
JAXBContext jc = JAXBContext.newInstance(clazz);
This improvement makes, in my case, that processing times drops from 60~70ms to just 5~10ms
I usually solve problems like this with a ThreadLocal class pattern.
Given the fact that you need a different marshaller for each Class, you can combine it with a singleton-map pattern.
To save you 15 minutes, of work. Here follows my implementation of a thread-safe Factory for Jaxb Marshallers and Unmarshallers.
It allows you to access the instances as follows ...
Marshaller m = Jaxb.get(SomeClass.class).getMarshaller();
Unmarshaller um = Jaxb.get(SomeClass.class).getUnmarshaller();
And the code you will need is a little Jaxb class that looks as follows:
public class Jaxb
{
// singleton pattern: one instance per class.
private static Map<Class,Jaxb> singletonMap = new HashMap<>();
private Class clazz;
// thread-local pattern: one marshaller/unmarshaller instance per thread
private ThreadLocal<Marshaller> marshallerThreadLocal = new ThreadLocal<>();
private ThreadLocal<Unmarshaller> unmarshallerThreadLocal = new ThreadLocal<>();
// The static singleton getter needs to be thread-safe too,
// so this method is marked as synchronized.
public static synchronized Jaxb get(Class clazz)
{
Jaxb jaxb = singletonMap.get(clazz);
if (jaxb == null)
{
jaxb = new Jaxb(clazz);
singletonMap.put(clazz, jaxb);
}
return jaxb;
}
// the constructor needs to be private,
// because all instances need to be created with the get method.
private Jaxb(Class clazz)
{
this.clazz = clazz;
}
/**
* Gets/Creates a marshaller (thread-safe)
* #throws JAXBException
*/
public Marshaller getMarshaller() throws JAXBException
{
Marshaller m = marshallerThreadLocal.get();
if (m == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
m = jc.createMarshaller();
marshallerThreadLocal.set(m);
}
return m;
}
/**
* Gets/Creates an unmarshaller (thread-safe)
* #throws JAXBException
*/
public Unmarshaller getUnmarshaller() throws JAXBException
{
Unmarshaller um = unmarshallerThreadLocal.get();
if (um == null)
{
JAXBContext jc = JAXBContext.newInstance(clazz);
um = jc.createUnmarshaller();
unmarshallerThreadLocal.set(um);
}
return um;
}
}
Creating JAXBContext within a enum and accessing it within application Thread to create Marshaller/Unmarshaller will suffice.
public enum MyApplicationJAXBContext {
REQ(Request.class), RESP(Response.class);
private final JAXBContext jaxbContext;
MyApplicationJAXBContext(Class contextClass) {
try {
jaxbContext = JAXBContext.newInstance(contextClass);
} catch (JAXBException e)
throw new RunTimeException(e);
// Lets caller decide what to do ?
}
}
public JAXBContext getJaxbContext() {
return jaxbContext;
}
}
public class MyAppCallable implements Callable<Response> {
private final Request req;
public MyAppCallable(Request req) {
this.req = req;
}
public Response call() {
Marshaller marshaller = MyApplicationJAXBContext.REQ.getJaxbContext().createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// Anything else you want to configure properties
Unmarshaller unmarshaller = MyApplicationJAXBContext.RESP.getJaxbContext().createUnmarshaller();
/**
All other logic you want to do after req/rsp usage and return Response
**/
}
}

JAXB Nesting a Collection

I'm having troubles mapping a Collection of JAXB object within another JAXB object, anyone see the issue with my structure below? I get an empty formerUsers ArrayList using the following code:
String test="<SSO-Request><User-Id>3119043033121014002</User-Id><Former-User-Ids><User-Id>3119043033121014999</User-Id><User-Id>3119043033121014555</User-Id></Former-User-Ids></SSO-Request>";
SSORequest ssoRequest=null;
try{
JAXBContext jaxbContext = JAXBContext.newInstance(SSORequest.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
ssoRequest = (SSORequest) unmarshaller.unmarshal(new StringReader(test));
}
catch(Exception e){
e.printStackTrace();
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="SSO-Request")
public class SSORequest {
#XmlElement(name="User-Id")
String userId;
#XmlElementWrapper(name="Former-User-Ids")
List<FormerUser> formerUsers;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="Former-User-Ids")
public class FormerUser {
#XmlElement(name="User-Id")
String userId;
}
You're over-complicating your mapping, this is all you need:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="SSO-Request")
public class SSORequest {
#XmlElement(name="User-Id")
String userId;
#XmlElementWrapper(name="Former-User-Ids")
#XmlElement(name="User-Id")
List<String> formerUserIds;
}
You should either change your mapping as skaffman proposed, or you should change the xml:
<SSO-Request><User-Id>3119043033121014002</User-Id><Former-User-Ids><Former-User><User-Id>3119043033121014999</User-Id></Former-User><Former-User><User-Id>3119043033121014555</User-Id></Former-User></Former-User-Ids></SSO-Request>
and change the name of the the FormerUser xml element:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="Former-User")
public class FormerUser {
#XmlElement(name="User-Id")
String userId;
}
If the property should be List<FormerUser> then you will need a way to tell JAXB what the ID corresponds to. If the data for FormerUsers will occur in the document then you can use #XmlID and #XmlIDREF for this mapping:
http://bdoughan.blogspot.com/2010/10/jaxb-and-shared-references-xmlid-and.html
If the data for FormerUsers will occur outside the document, then you can use the strategy I described in the answer below:
Using JAXB to cross reference XmlIDs from two XML files

Categories