Simple way to stub an element with JAXB? - java

I'm just starting to learn to use JAXB to marshal my java objects into XML. I've got no problem serializing objects, but now what I want to do is insert an element into the xml for which there is no corresponding POJO.
For example, I have a list of Folders, and I want to put each Folder into an xml element called Folders. Does this make sense? I could just write <Folders> and </Folders> to the output stream where appropriate, but I'd like to do this in a more formal way.

You could use JAXB with StAX to do this. Using a XMLStreamWriter will help formalize the interaction with the XML and give you better control over such this as encodings:
package forum8406266;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamWriter;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Folder.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
List<Folder> folders = new ArrayList<Folder>(3);
folders.add(new Folder());
folders.add(new Folder());
folders.add(new Folder());
XMLOutputFactory xof = XMLOutputFactory.newFactory();
XMLStreamWriter xsw = xof.createXMLStreamWriter(System.out);
xsw.writeStartDocument();
xsw.writeStartElement("Folders");
for(Folder folder : folders) {
marshaller.marshal(folder, xsw);
}
xsw.writeEndElement();
xsw.writeEndDocument();
xsw.flush();
}
}
The above code assumes that your Folder class is annotated with #XmlRootElement:
package forum8406266;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Folder")
public class Folder {
}

Related

FasterXML jackson-dataformat-xml serialization version and encoding not added to xml

Hi I am serializing java POJO object to xml using faster-xml(https://github.com/FasterXML/jackson-dataformat-xml/wiki ).When i do that i got xml but it doesn't have any version and encoding in xml file.This is the format i need
<?xml version="1.0" encoding="utf-8"?>
<SampleRequest>
...
</SampleRequest>
But I got Only this one
<SampleRequest>
...
</SampleRequest>
Is there any configuration need to be added in jackson fasterxml anotation.
You can configure your XmlMapper to write the XML header.
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure( ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true );
As an example:
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import java.io.IOException;
public class Xml {
public static void main(String[] args) throws IOException {
// Important: create XmlMapper; it will use proper factories, workarounds
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
xmlMapper.writeValue(System.out, new SampleRequest());
}
}
class SampleRequest{
public int x = 1;
public int y = 2;
}
This generates the output:
<?xml version="1.0" encoding="UTF-8"?>
<SampleRequest>
...
</SampleRequest>
In case you want to set the version to 1.1 instead of 1.0, use ToXmlGenerator.Feature.WRITE_XML_1_1.
Notice that Faster-XML team recommends to use Woodstox library. In case you use it, some other configurations can be set. Among all of them there is one related to setting double quotes:
public static final String P_USE_DOUBLE_QUOTES_IN_XML_DECL="com.ctc.wstx.useDoubleQuotesInXmlDecl";
at WstxOutputProperties.java
For more details check out configuring Woodstox parser.
For those wondering how to change single quotes to double quotes:
String propName = com.ctc.wstx.api.WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL;
xmlMapper.getFactory()
.getXMLOutputFactory()
.setProperty(propName, true);
import com.ctc.wstx.api.WstxOutputProperties;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
#Configuration
public class XmlConfig {
#Bean
public MappingJackson2XmlHttpMessageConverter mappingJackson2XmlHttpMessageConverter(Jackson2ObjectMapperBuilder builder) {
XmlMapper xmlMapper = builder.createXmlMapper(true).build();
xmlMapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION);
xmlMapper.getFactory().getXMLOutputFactory().setProperty(WstxOutputProperties.P_USE_DOUBLE_QUOTES_IN_XML_DECL, true);
return new MappingJackson2XmlHttpMessageConverter(xmlMapper);
}
}

Is it possible to programmatically configure JAXB?

Say I have two JavaBeans Person and Address.
If I create a list of Person objects, I'd like to marshal to something like this:
<persons>
<person>...</person>
</persons>
It's possible to use the technique described here:
Using JAXB to unmarshal/marshal a List<String>
By annotating JaxbList with #XmlRootElement(name = "persons") and #XmlElement(name = "person"), then it's possible to marshal to the XML above.
But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:
<list>
<item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>
But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.
So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside #XmlRootElement and #XmlElement?
Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?
Update
#Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):
//JAXBContext is thread safe and so create it in constructor or
//setter or wherever:
...
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
...
public String marshal(List<T> things, Class clazz) {
//configure JAXB and marshaller
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//Create wrapper based on generic list of objects
Wrapper<T> wrapper = new Wrapper<T>(things);
JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);
StringWriter result = new StringWriter();
//marshal!
m.marshal(wrapperJAXBElement, result);
return result.toString();
}
You could create a generic Wrapper object like the following:
Wrapper
You could create a generic wrapper class with a List property annotated with #XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).
package forum13272288;
import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;
public class Wrapper<T> {
private List<T> items = new ArrayList<T>();
#XmlAnyElement(lax=true)
public List<T> getItems() {
return items;
}
}
Address
You will need to annotate the possible contents of the list with #XmlRootElement.
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Address {
}
Person
package forum13272288;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Person {
}
Demo
The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the #XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).
package forum13272288;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
marshaller.marshal(wrapper1, System.out);
StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
marshaller.marshal(wrapper2, System.out);
}
}
Output
Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
<person/>
<person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
<address/>
<address/>
</addresses>
For More Information
http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html

Update partial XML mapping to bean

I need to map an xml file subset of nodes to a Java Bean.
For example map
<data>
<field1>Value</field1>
<field2>Value</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>Value</field5>
</data>
to
public class DataBean {
private String field2;
private String field5;
// ...getter/setter
}
then manipulate the bean and update the source xml file without loosing elements that are not mapped.
How can I use to do it?
What library?
Thanks for help,
Maurizio
Note: I'm the EclipseLink JAXB (MOXy) lead an a member of the JAXB 2 (JSR-222) expert group.
Below is how this can be done with MOXy's implementation of the JAXB Binder:
DataBean
package forum9988170;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="data")
public class DataBean {
private String field2;
private String field5;
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
public String getField5() {
return field5;
}
public void setField5(String field5) {
this.field5 = field5;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file named jaxb.properties in the same package as your domain classes with the following entry,
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum9988170;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(DataBean.class);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
File xml = new File("src/forum9988170/input.xml");
Document document = db.parse(xml);
Binder<Node> binder = jc.createBinder();
DataBean dataBean = (DataBean) binder.unmarshal(document);
dataBean.setField2("NEW FIELD 2");
dataBean.setField5("NEW FIELD 5");
binder.updateXML(dataBean);
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMSource source = new DOMSource(document);
StreamResult result = new StreamResult(System.out);
t.transform(source, result);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<data>
<field1>Value</field1>
<field2>NEW FIELD 2</field2>
<field3>Value</field3>
<field4>Value</field4>
<field5>NEW FIELD 5</field5>
</data>
For More Information
http://blog.bdoughan.com/2010/09/jaxb-xml-infoset-preservation.html
http://blog.bdoughan.com/2012/01/how-does-jaxb-compare-to-xmlbeans.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If you decide what is in structure xml then you may use XStream (http://x-stream.github.io/) to serialize and deserialize.
But if you only deserialize from xml to bean (from foreign format), then you should use Smooks (http://www.smooks.org/).
Both of these libraries are very light in contrast to JAXB. JAXB is not flexible and requires that creating XML Schema. I do not recommend, because you lose more time than on creating a simple DOM parse.
JAXB is very academic. Example: many of "SOAP envelopes" is not fully described by WSDL documents, but adds some xml into WSDL field (in a simple text field). In such a case, you lose a lot of time to create a JAXB infrastructure...
Of course this is just my personal opinion. But remember these two tools and try to use them. You'll see that it really worth.

JAXB: How to customize Xml serialization of double fields

I have a legacy class, with a lot of public double fields. All double fields are initialized with Double.MAX_VALUE to indicate that they are empty. (The legacy serialization is coded to ignore the field and not serialize if field is equals to Double.MAX_VALUE).
We are now trying to serialize this class to Xml using JAXB Marshaller. It is working fine, except that we want to prevent generating Xml for fields which equal Double.MAX_VALUE.
We aren't using a separate JAXB schema, just marking up our classes with various javax.xml.bind.annotation Annotations. If a schema is used, you can add a <javaType> element to specify a custom DataType converter. Is there any way to do this using Annotations or programmatically?
After trying approach recommended below, I still can't get XmlAdapter picked up:
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(value=EmptyDoubleValueHandler.class, type=Double.class), #XmlJavaTypeAdapter(value=EmptyDoubleValueHandler.class, type=double.class)})
package tta.penstock.data.iserver;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
My top level class is: tta.penstock.data.iserver.OrderBlotter, which contains a list of tta.penstock.data.iserver.OrderResponseWrappers which extends com.eztech.OrderResponse. All the double fields are contained in com.eztech.OrderResponse.
My unit test code does the following:
JAXBContext context = JAXBContext.newInstance(new Class[] { OrderBlotter.class, OrderResponseWrapper.class, OrderResponse.class});
Marshaller marshaller = context.createMarshaller();
StringWriter stringWriter = new StringWriter();
marshaller.marshal(blotter, stringWriter);
System.out.println("result xml=\n" + stringWriter.toString());
But the double values still don't get handled by the XmlAdapter. I know I'm missing something basic, but I'm not sure what it is.
You could use an XmlAdapter:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
The XmlAdapter
package example;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DoubleAdapter extends XmlAdapter<Double, Double>{
#Override
public Double unmarshal(Double v) throws Exception {
return v;
}
#Override
public Double marshal(Double v) throws Exception {
if(Double.MAX_VALUE == v) {
return null;
} else {
return v;
}
}
}
The Model Object
package example;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
public class Root {
#XmlJavaTypeAdapter(DoubleAdapter.class)
public Double maxDouble = Double.MAX_VALUE;
#XmlJavaTypeAdapter(DoubleAdapter.class)
public Double aDouble = 123d;
}
Demo Code
package example;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(new Root(), System.out);
}
}
UPDATE
StaxMan's suggestion is a good one. If you specify the following package level annotation you can avoid the need of individually annotating all the Double properties
package-info.java
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type=Double.class, value=DoubleAdapter.class)
})
package example;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
Write a getter that returns null, instead of Double.MAX_VALUE? (if type is 'double', need to change it to 'Double' first, to allow nulls).
Since JAXB by default ignores writing out of nulls, that should achieve what you are trying to do. This assumes you can modify legacy class in question.

JAXB XML output format questions

I have Java classes with the following structure (the class names do not imply anything, I was just making them up).
package test;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
#XmlRootElement
public class Test
{
#XmlAccessorType(XmlAccessType.FIELD)
static class Machine
{
#XmlElementWrapper(name="servers")
#XmlElement(name="server")
List<Server> servers = new ArrayList<Server>();
}
#XmlAccessorType(XmlAccessType.FIELD)
static class Server
{
Threshold t = new Threshold();
}
#XmlAccessorType(XmlAccessType.FIELD)
static class Threshold
{
RateThreshold load = new RateThreshold();
}
#XmlAccessorType(XmlAccessType.FIELD)
static class RateThreshold
{
#XmlAccessorType(XmlAccessType.FIELD)
static class Rate
{
int count;
Period period = new Period();
}
#XmlAccessorType(XmlAccessType.FIELD)
private static class Period
{
#XmlAttribute
private String type = "second";
#XmlValue
private float period;
}
Rate min = new Rate();
Rate max = new Rate();
}
#XmlElementWrapper(name="machines")
#XmlElement(name="machine")
List<Machine> machines = new ArrayList<Machine>();
public static void main(String[] args)
{
Machine m = new Machine();
Server s = new Server();
s.t.load.max.count = 10;
s.t.load.min.count = 1;
m.servers.add(s);
Test t = new Test();
t.machines.add(m);
JAXBContext jaxbContext;
Marshaller marshaller;
try
{
jaxbContext = JAXBContext.newInstance(Test.class);
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(t, System.out);
}
catch (JAXBException e)
{
e.printStackTrace();
}
}
}
The problem I am having is with the XML output generated by JAXB when marshalling a Test instance. The XML output would always look like the following:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<test>
<machines>
<machine>
<servers>
<server>
<t>
<load>
<min>
<count>1</count>
<period type="second">0.0</period>
</min>
<max>
<count>10</count>
<period type="second">0.0</period>
</max>
</load>
</t>
</server>
</servers>
</machine>
</machines>
</test>
As you can see, some elements are not being indented properly (that is, the deepest elements, count and period). Why is that? Is there something wrong with the way I created the JAXB context? Or is there a maximum limit to how many elements that can be indented recursively by JAXB? How could I fix this? Note that I have also set JAXB_FORMATTED_OUTPUT to true, but still get the improper indentation.
Thanks.
Indenting occurs modulo 8, in
com.sun.xml.bind.v2.runtime.output.IndentingUTF8XmlOutput
you find
int i = depth%8;
One of the overloads of the marshal() method of the marshaler accepts an XMLStreamWriter, so you can bypass the brain-damaged formatting mechanism of the Reference Implementation of JAXB by writing your own formatting XML stream writer. You would end up doing something like this:
public static void SaveContainer( Container container, OutputStream stream ) throws ...
{
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter( stream, "UTF-8" );
writer = new MyAwesomeCoolFormattingXMLStreamWriter( writer );
marshaller.marshal( container, writer );
}
I don't think there's a limit. I've seen very deep nesting, without any difficulties. Do you have any whitespace control in place? Also, you haven't provided the definition of the RateThreshold class, which is the one creating the unexpected output.

Categories