Can jaxb resolve system properties? - java

We use JAXB to configure object with XML. I was wondering if there is a way that JAXB could resolve system property. For instance, if I have a bean with property color, I would like to be able to do that:
<mybean color="${mycolor.in.data.property}" />
But if I do that, JAXB create the mybean object an color will be equals to the string:
mycolor.in.data.property
Is there is any equivalent of the PropertyPlaceholderConfigurer in spring for JAXB, so that my system property could be resolved?

An XmlAdapter is a JAXB (JSR-222) mechanism that allows you to convert an object to another object during marshalling/unmarshaller. You could write an XmlAdapter that converts the system property name to the real value.
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ColorAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
return System.getProperty(v);
}
#Override
public String marshal(String v) throws Exception {
return v;
}
}
Then you use the #XmlJavaTypeAdapter annotation to configure the XmlAdapter for your property.
#XmlJavaTypeAdapter(ColorAdapter.class)
public String getColor() {
return color;
}
For More Information
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
UPDATE
Ok thanks. Actually I do not have access to the class, as this is part
of an imported library. So I was more looking of a way to configure
this directly in the xml file, but it is probably not possible.
If you can't modify the class, then you can use a StreamReaderDelegate to modify the XML input. There a few methods that deal with the text/character data so you may need to experiment to be sure that you are overriding the one that works best with the JAXB implementation you are using.
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(MyBean.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource source = new StreamSource("input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(source);
xsr = new StreamReaderDelegate(xsr) {
#Override
public String getText() {
String text = super.getText();
if(text.contains("${")) {
text = System.getProperty(text);
}
return text;
}
};
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(xsr);
}
}

Related

Unmarshalling a simple XML

I am having hard time unmarshalling the following XML using JAXB. This is the xml which is having only one field with an attribute. I have referred many tutorials where they only do an example of reading a string in fields which s not helpful in my case.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CircuitImpactConfigs id="1">
<Objects>
<Object type="1" impactAnalysisDataBuilderClassName="tttttttt"/>
<Object type="2" impactAnalysisDataBuilderClassName="yyyyyyyyy"/>
<Object type="3" impactAnalysisDataBuilderClassName="eeeeeee" />
<Object type="4" impactAnalysisDataBuilderClassName="iiiiiiiii"/>
<Object type="5" impactAnalysisDataBuilderClassName="rrrrrrrrrr"/>
<Object type="6" impactAnalysisDataBuilderClassName="zzzzzz"/>
<Object type="7" impactAnalysisDataBuilderClassName="qqqqqqqqqqq"/>
</Objects>
<ForceSwitchMode name="FORCE_SWITCHED" />
</CircuitImpactConfigs>
Based on what i learnt from tutorial
My Classes are CircuitImpactConfigs.java
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "CircuitImpactConfigs")
#XmlAccessorType(XmlAccessType.FIELD)
public class CircuitImpactConfigs {
private ForceSwitchMode ForceSwitchMode;
List<Obj> Object;
#XmlElement
public List<Obj> getObject() {
return Object;
}
public void setObject(List<Obj> object) {
Object = object;
}
#XmlElement
public ForceSwitchMode getForceSwitchMode() {
return ForceSwitchMode;
}
public void setForceSwitchMode(ForceSwitchMode forceSwitchMode) {
ForceSwitchMode = forceSwitchMode;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class ForceSwitchMode {
private String name;
#XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
import javax.xml.bind.annotation.XmlAttribute;
public class Obj {
String type;
String impactAnalysisDataBuilderClassName;
#XmlAttribute
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#XmlAttribute
public String getImpactAnalysisDataBuilderClassName() {
return impactAnalysisDataBuilderClassName;
}
public void setImpactAnalysisDataBuilderClassName(String impactAnalysisDataBuilderClassName) {
this.impactAnalysisDataBuilderClassName = impactAnalysisDataBuilderClassName;
}
}
I am getting null list when doing the unmarshalling. This is the class where i create the JAXBcontext object and create unmarshalling object.
import java.io.File;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
public class CircuitImpactConfUnmarshaller {
public static void main(String[] args) throws JAXBException {
File file = new File("CircuitImpact.xml");
System.out.println(file.exists());
JAXBContext jaxbContext = JAXBContext.newInstance(CircuitImpactConfigs.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CircuitImpactConfigs que = (CircuitImpactConfigs) jaxbUnmarshaller.unmarshal(file);
System.out.println(que.getForceSwitchMode().getName());
List<Obj> list = que.getObject();
System.out.println(list);
}
}
the last print statement is giving null. I understand i am doing something wrong in the class Obj
JAXB uses implicit naming conventions and explicit annotations to define a mapping between a XML and a Java structure.
Either element and attribute names in the XML match field names in Java (match by naming convention) or you need to use annotations to establish the mapping.
The Java list CircuitImpactConfigs.Object is not getting filled because the mapping failed, since the corresponding element in the XML is named Objects.
You can now either rename CircuitImpactConfigs.Object to CircuitImpactConfigs.Objects or use the name parameter of a JAXB annotation to define the corresponding name:
#XmlElement(name="Objects")
public List<Obj> getObject() {
EDIT: As you indicate in your comments there are still other mapping issues with your code. I would suggest that you adapt another approach:
Create a CircuitImpactConfigs object with all subobjects filled.
Marhsall that object to a XML file.
Check that the XML is in the expected format. If not, tweak the mapping.
Following this approach you can be sure that a XML file in the desired format can be unmarshalled to your Java structure. The code to marshall is
CircuitImpactConfigs que = ...
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(que, System.out);

How can I make a class field become a tag name using JAXB?

I am using Java and JAXB for XML processing.
I have the following class:
public class Characteristic {
private String characteristic;
private String value;
#XmlAttribute
public String getCharacteristic() {
return characteristic;
}
public void setCharacteristic(String characteristic) {
this.characteristic = characteristic;
}
#XmlValue
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
public static void main(String[] args) {
Characteristic c = new Characteristic();
c.setCharacteristic("store_capacity");
c.setValue(40);
Characteristic c2 = new Characteristic();
c2.setCharacteristic("number_of_doors");
c2.setValue(4);
}
This is the result that I get:
<characteristics characteristic="store_capacity">40</characteristics>
<characteristics characteristic="number_of_doors">4</characteristics>
I want to get the following result:
<store_capacity>40</store_capacity>
<number_of_doors>4</number_of_doors>
How can I achieve this?
You can use a combination of #XmlElementRef and JAXBElement to produce dynamic element names.
The idea is:
Make Characteristic a subclass of JAXBElement and override the getName() method to return the name based on the characteristic property.
Annotate characteristics with #XmlElementRef.
Provide the #XmlRegistry (ObjectFactory) with an #XmlElementDecl(name = "characteristic").
Below is a working test.
The test itself (nothing special):
#Test
public void marshallsDynamicElementName() throws JAXBException {
JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
final Characteristics characteristics = new Characteristics();
final Characteristic characteristic = new Characteristic(
"store_capacity", "40");
characteristics.getCharacteristics().add(characteristic);
context.createMarshaller().marshal(characteristics, System.out);
}
Produces:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<characteristics><store_capacity>40</store_capacity></characteristics>
Let's start with the characteristics root element class. It has a characteristics property which is annotated with #XmlElementRef. This means that the contents should be either JAXBElements or #XmlRootElement-annotated class instances.
#XmlRootElement(name = "characteristics")
public class Characteristics {
private final List<Characteristic> characteristics = new LinkedList<Characteristic>();
#XmlElementRef(name = "characteristic")
public List<Characteristic> getCharacteristics() {
return characteristics;
}
}
In order for this to work you also need an ObjectFactory or something annotated with #XmlRegistry having a corresponding #XmlElementDecl:
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name = "characteristic")
public JAXBElement<String> createCharacteristic(String value) {
return new Characteristic(value);
}
}
Recall, the characteristics property must contain either #XmlRootElement-annotated class instances or JAXBElements. #XmlRootElement is not suitable since it's static. But JAXBElement is dynamic. You can subclass JAXBElement and override the getName() method:
public class Characteristic extends JAXBElement<String> {
private static final long serialVersionUID = 1L;
public static final QName NAME = new QName("characteristic");
public Characteristic(String value) {
super(NAME, String.class, value);
}
public Characteristic(String characteristic, String value) {
super(NAME, String.class, value);
this.characteristic = characteristic;
}
#Override
public QName getName() {
final String characteristic = getCharacteristic();
if (characteristic != null) {
return new QName(characteristic);
}
return super.getName();
}
private String characteristic;
#XmlTransient
public String getCharacteristic() {
return characteristic;
}
public void setCharacteristic(String characteristic) {
this.characteristic = characteristic;
}
}
In this case I've overridden the getName() method to dynamically determine the element name. If characteristic property is set, its value will be used as the name, otherwise the method opts to the default characteristic element.
The code of the test is available on GitHub.
If you are using EclipseLink MOXy as your JAXB (JSR-222) provider then you can leverage our #XmlVariableNode extension for this use case (see: http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-json-schema.html):
Java Model
Characteristics
We will leverage the #XmlVariableNode extension here. This annotation specifies a field/property from the reference class to be used as the element name.
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Characteristics {
#XmlVariableNode("characteristic")
private List<Characteristic> characteristics;
}
Characteristic
We need to mark the characteristic field/property as #XmlTransient so it won't appear as a child element.
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Characteristic {
#XmlTransient
private String characteristic;
#XmlValue
private String value;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to have EclipseLink on your classpath and include a file called jaxb.properties with the following content in the same package as your domain model (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo Code
Demo
Here is some demo code that will read/write the desired XML. Note how the standard JAXB APIs are used.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Characteristics.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Characteristics characteristics = (Characteristics) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(characteristics, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<characteristics>
<store_capacity>40</store_capacity>
<number_of_doors>4</number_of_doors>
</characteristics>
The following approach will work with any JAXB (JSR-222) implementation.
Java Model
Characteristics
We will leverage an XmlAnyElement annotation. This annotation gives us alot of flexibility on what type of data can be held, including DOM nodes. We will use an XmlAdapter to convert instances of Characteristic to DOM nodes.
import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Characteristics {
#XmlAnyElement
#XmlJavaTypeAdapter(CharacteristicAdapter.class)
private List<Characteristic> characteristics;
}
Characteristic
As far as JAXB is concerned this class is no longer part of our model.
public class Characteristic {
String characteristic;
String value;
}
CharacteristicAdapter
This XmlAdapter converts the Characteristic object to and from a DOM node allowing us to construct it as we like.
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.*;
public class CharacteristicAdapter extends XmlAdapter<Object, Characteristic> {
private Document doc;
public CharacteristicAdapter() {
try {
doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
} catch(Exception e) {
throw new RuntimeException(e);
}
}
#Override
public Characteristic unmarshal(Object v) throws Exception {
Element element = (Element) v;
Characteristic characteristic = new Characteristic();
characteristic.characteristic = element.getLocalName();
characteristic.value = element.getTextContent();
return characteristic;
}
#Override
public Object marshal(Characteristic v) throws Exception {
Element element = doc.createElement(v.characteristic);
element.setTextContent(v.value);
return element;
}
}
Demo Code
Demo
Below is some code that will read/write the desired XML. Note that the setAdapter call on Marshaller is not required, but is a performance improvement since it will cause the XmlAdapter to be reused.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Characteristics.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Characteristics characteristics = (Characteristics) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setAdapter(new CharacteristicAdapter());
marshaller.marshal(characteristics, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<characteristics>
<store_capacity>40</store_capacity>
<number_of_doors>4</number_of_doors>
</characteristics>
you can specify your class as :
#XmlRootElement(name = "Characteristic")
public class Characteristic {
#XmlElement(name = "store_capacity")
protected String storeCapacity;
#XmlElement(name = "number_of_doors")
protected String numberOfDoors;
/** getters and setters of above attributes **/
}
EDIT : If you wants the attributes to be dynamic then you can refer below link
getting Dynamic attribute for element in Jaxb

Jaxb marshalling with custom annotations

I have a requirement, to marshall/unmarshall some elements of java pojo depending upon a custom annotation marked on the field.
suppose there are 3 fields in my java pojp
#CustomVersion("v1")
private String field1;
#CustomVersion("v1","v2")
private String field2;
#CustomVersion("v2")
private String field3;
i would like to marshall only the fields with v1 if i pass version="v1" parameter while conversion in jaxb. if i pass v2, all fields with v2 annotation should only be marshalled.
is that even possible using jaxb? i am sure selective marshalling would be supported through some library or way, am not still able to figure it out after quite some searching.
any help or advice or pointers are highly appreciated.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below is an example of how you could use MOXy's #XmlNamedObjectGraphs extension to map your use case.
Java Model
Foo
The #XmlNamedObjectGraphs extension allows you to specify multiple subsets of mappings identified by a key.
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlNamedObjectGraphs({
#XmlNamedObjectGraph(
name="v1",
attributeNodes = {
#XmlNamedAttributeNode("field1"),
#XmlNamedAttributeNode("field2")}),
#XmlNamedObjectGraph(
name="v2",
attributeNodes = {
#XmlNamedAttributeNode("field2"),
#XmlNamedAttributeNode("field3")})
})
public class Foo {
private String field1 = "ONE";
private String field2 = "TWO";
private String field3 = "THREE";
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo Code
Demo
You can specify the key corresponding to the object graph to have that subset applied to the object you are marshalling.
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Foo foo = new Foo();
// Marshal Everything
marshaller.marshal(foo, System.out);
// Marshal "v1" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v1");
marshaller.marshal(foo, System.out);
// Marshal "v2" Data
marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v2");
marshaller.marshal(foo, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field1>ONE</field1>
<field2>TWO</field2>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<field2>TWO</field2>
<field3>THREE</field3>
</foo>
For More Information
http://blog.bdoughan.com/2013/03/moxys-object-graphs-inputoutput-partial.html
First of all I would suggest doing such preprocessing before marshalling. It would be much easier. However if it is not possible for some reason then you can create you custom type adapter. Then you can put #XmlJavaTypeAdapter(VersioningAdapter.class) on every type that you want to have versioning enabled.
#XmlJavaTypeAdapter can also be specified on package level, but you have to specify to which types it applies. You cannot use XmlAdapter without specifying somewhere #XmlJavaTypeAdapter.
Drawbacks of such solution:
if you have multiple versioned types then each of them has to be annotated with #XmlJavaTypeAdapter
#XmlJavaTypeAdapter does not work for root element, only on child elements. You have to call adapter manually on root element before marshalling
AFAIK there is no other option for customizing JAXB marshalling. That's why I think that annotation processing should be performed in separate step before marshalling. Unless you can accept mentioned limitations.
Sample adapter (full code can be found here):
public class VersioningAdapter extends XmlAdapter<Object, Object> {
#Override
public Object unmarshal(Object v) throws Exception {
// TODO Auto-generated method stub
return null;
}
#Override
public Object marshal(Object v) throws Exception {
if (v == null) {
return v;
}
Field[] fields = v.getClass().getDeclaredFields();
for (Field field : fields) {
Annotation[] annotations = field.getDeclaredAnnotations();
CustomVersion annotation = findCustomVersion(annotations);
if (annotation != null) {
if (!contains(annotation, Configuration.getVersion())) {
field.setAccessible(true);
field.set(v, null);
}
}
}
return v;
}
private CustomVersion findCustomVersion(Annotation[] annotations) {
for (Annotation annotation : annotations) {
if (annotation instanceof CustomVersion) {
return (CustomVersion) annotation;
}
}
return null;
}
private boolean contains(CustomVersion annotation, String version) {
String[] values = annotation.value();
for (String value : values) {
if (value.equals(version)) {
return true;
}
}
return false;
}
}

How can JAXB XmlAdapter be used to marshall lists?

Instances of this class are part of a large object graph and are not at the root of the object graph:
public class Day
{
public Day(LocalDate date, List<LocalTime> times)
{
this.date = date;
this.times = times;
}
public Day()
{
this(null, null);
}
public LocalDate getDate()
{
return date;
}
public List<LocalTime> getTimes()
{
return times;
}
private final LocalDate date;
private final List<LocalTime> times;
}
The object graph is converted to JSON using Jersey and JAXB. I have XmlAdapters registered for LocalDate and LocalTime.
The problem is that it's only working for the date property and not the times property. I suspect this has something to do with the fact that times is a list rather than a single value. How, then, do I tell Jersey/JAXB to marshall each element in the times list using the registered XmlAdapter?
Update:
I confirmed that LocalTime marshalling is indeed working for scalar LocalTime properties by adding a scalar LocalTime property and observing the expected output in the JSON.
For completeness, here's package-info.java:
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(value = LocalDateAdapter.class, type = LocalDate.class),
#XmlJavaTypeAdapter(value = LocalTimeAdapter.class, type = LocalTime.class)
})
package same.package.as.everything.else;
LocalDateAdapter:
public class LocalDateAdapter extends XmlAdapter<String, LocalDate>
{
#Override
public LocalDate unmarshal(String v) throws Exception
{
return formatter.parseLocalDate(v);
}
#Override
public String marshal(LocalDate v) throws Exception
{
return formatter.print(v);
}
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd");
}
LocalTimeAdapter:
public class LocalTimeAdapter extends XmlAdapter<String, LocalTime>
{
#Override
public LocalTime unmarshal(String v) throws Exception
{
return formatter.parseLocalTime(v);
}
#Override
public String marshal(LocalTime v) throws Exception
{
return formatter.print(v);
}
private final DateTimeFormatter formatter = DateTimeFormat.forPattern("HHmm");
An XmlAdapter for a class is applied to a mapped field/property of that type, and in the case of collections, for each item in the collection. The example below proves that this works. Have you tried running your example standalone to XML to verify the mappings that way. Is suspect the problem is something else other than the XmlAdapter specifically.
StringAdapter
The following XmlAdapter will convert a String to lower case on the unmarshal operation and convert it to upper case when marshalled.
package forum14569293;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
return v.toLowerCase();
}
#Override
public String marshal(String v) throws Exception {
return v.toUpperCase();
}
}
package-info
Just as in your question a package level #XmlJavaTypeAdapters annotation will be used to register the XmlAdapter. This will register this XmlAdapter for all mapped String properties within this package (see: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(value=StringAdapter.class, type=String.class)
})
package forum14569293;
import javax.xml.bind.annotation.adapters.*;
Root
Below is a sample domain model similar to your Day class with two mapped properties. The first is of type String and the second List<String>. One thing I notice about your Day class is that you only have get methods. This means that you will need to add an #XmlElement annotation for a JAXB impl to consider that a mapped property.
package forum14569293;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Root {
public Root(String foo, List<String> bar) {
this.foo = foo;
this.bar = bar;
}
public Root() {
this(null, null);
}
#XmlElement
public String getFoo() {
return foo;
}
#XmlElement
public List<String> getBar() {
return bar;
}
private final String foo;
private final List<String> bar;
}
Demo
package forum14569293;
import java.util.*;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
List<String> bar = new ArrayList<String>(3);
bar.add("a");
bar.add("b");
bar.add("c");
Root root = new Root("Hello World", bar);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code we see that all the strings were converted to upper case by the XmlAdapter.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bar>A</bar>
<bar>B</bar>
<bar>C</bar>
<foo>HELLO WORLD</foo>
</root>
UPDATE
Thanks. I tried it and the XML consisted of one empty tag only,
meaning there's something about the POJO model that JAXB doesn't like.
(Perhaps it should be Serializable?)
JAXB does not require that POJOs implement Serializable.
That's interesting because it seems to indicate that the only part
JAXB plays in this is to lend its annotations and some other
interfaces (e.g. XmlAdapter) to the JSON (de)serializer and that's
where the relationship ends.
It depends on what is being used as the JSON binding layer. The JAXB (JSR-222) specification does not cover JSON-binding so this type of support is beyond the spec. EclipseLink JAXB (MOXy) offers native JSON-binding (I'm the MOXy lead) with it you could do something like:
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
marshaller.marshal(root, System.out);
And get the following output that takes the XmlAdapter into account:
{
"bar" : [ "A", "B", "C" ],
"foo" : "HELLO WORLD"
}
Nevertheless, when I get an opportunity I will do what I can to get
JAXB to generate the XML and that may reveal something else.
This would be useful as I do not believe what you are seeing is a JAXB issue, but an issue in the JSON binding layer that you are using.

Marshalling/unmarshalling fields to tag with attributes using JAXB

Let's say I have class Example:
class Example{
String myField;
}
I want to unmarshal it in this way:
<Example>
<myField value="someValue" />
</Example>
Is it possible to unmarshal object in such way using JAXB XJC? ( I know about XmlPath in EclipseLink, but can't use it).
You could leverage an XmlAdapter for this use case. In that XmlAdapter you will convert a String to/from an object that has one property mapped to an XML attribute.
XmlAdapter
package forum12914382;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyFieldAdapter extends XmlAdapter<MyFieldAdapter.AdaptedMyField, String> {
#Override
public String unmarshal(AdaptedMyField v) throws Exception {
return v.value;
}
#Override
public AdaptedMyField marshal(String v) throws Exception {
AdaptedMyField amf = new AdaptedMyField();
amf.value = v;
return amf;
}
public static class AdaptedMyField {
#XmlAttribute
public String value;
}
}
Example
package forum12914382;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Example")
#XmlAccessorType(XmlAccessType.FIELD)
class Example{
#XmlJavaTypeAdapter(MyFieldAdapter.class)
String myField;
}
Demo
package forum12914382;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Example.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum12914382/input.xml");
Example example = (Example) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(example, System.out);
}
}
input.xml/Output
<Example>
<myField value="someValue" />
</Example>
Related Example
JAXB Element mapping
Yes, manually add the #XmlAttribute-Annotation or generate the classes from an XSD.

Categories

Resources