I use a Java class to generate the WSDL schema dynamically. I have this as one of my fields:
#XmlElements({
#XmlElement(name = "A", type = String.class),
#XmlElement(name = "B", type = Integer.class),
#XmlElement(name = "C", type = String.class),
#XmlElement(name = "D", type = String.class)
})
protected Object aOrBOrCOrD;
During marshalling, when the single choice property aOrBOrCOrD is set, which tag name(A, B, C or D) would be set in the XML?
Since there's only one field which would contain the data. And String could also mean any 1 of the 3 choice elements. How to get around this?
Can I split the single field in 4 and still maintain the choice property when the WSDL is generated somehow?
You could do the following:
Java Model
Foo
Instead of #XmlElements and a property of type Object, you can use #XmlElementRefs and a property of type JAXBElement. A JAXBElement allows you to preserve the element name.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementRefs({
#XmlElementRef(name = "A", type = JAXBElement.class),
#XmlElementRef(name = "B", type = JAXBElement.class),
#XmlElementRef(name = "C", type = JAXBElement.class),
#XmlElementRef(name = "D", type = JAXBElement.class)
})
protected JAXBElement<?> aOrBOrCOrD;
}
ObjectFactory
Along with #XmlElementRef you need to have a corresponding #XmlElementDec annotations on a class annotated with #XmlRegistry.
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
#XmlElementDecl(name="A")
public JAXBElement<String> createA(String value) {
return new JAXBElement<String>(new QName("A"), String.class, value);
}
#XmlElementDecl(name="B")
public JAXBElement<Integer> createB(Integer value) {
return new JAXBElement<Integer>(new QName("B"), Integer.class, value);
}
#XmlElementDecl(name="C")
public JAXBElement<String> createC(String value) {
return new JAXBElement<String>(new QName("C"), String.class, value);
}
#XmlElementDecl(name="D")
public JAXBElement<String> createD(String value) {
return new JAXBElement<String>(new QName("D"), String.class, value);
}
}
Demo Code
Demo
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, ObjectFactory.class);
StringReader xml = new StringReader("<foo><C>Hello World</C></foo>");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Foo foo = (Foo) unmarshaller.unmarshal(xml);
JAXBElement<?> aOrBOrCOrD = foo.aOrBOrCOrD;
System.out.println(aOrBOrCOrD.getName().getLocalPart());
System.out.println(aOrBOrCOrD.getDeclaredType());
System.out.println(aOrBOrCOrD.getValue());
}
}
Output
C
class java.lang.String
Hello World
Related
I know it doesn't make much sense, but I have to generate an XML from a Java object without the parent node of some elements, like explained below.
This is the example Java class model for the XML:
#XmlRootElement(name = "person")
public class PersonXml {
#XmlElement(name = "name")
private String name;
#XmlElement(name = "car")
private List<CarXml> cars;
.
#XmlRootElement(name = "car")
public class CarXml {
#XmlElement(name = "model")
private String model;
#XmlElement(name = "brand")
private String brand;
By default, if I generate the XML from an object of PersonXml like this:
StringWriter writer = new StringWriter();
JAXBContext ctx = JAXBContext.newInstance(PersonXml.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.marshal(xml, writer);
I would get:
<person>
<name>Pedro</name>
<car>
<model>Logan</model>
<brand>Renault</brand>
</car>
<car>
<model>Duster</model>
<brand>Renault</brand>
</car>
</person>
What I need is to remove the <car> tag, or even to prevent it to be generated at all.
I need the XML to be like this:
<person>
<name>Pedro</name>
<model>Logan</model>
<brand>Renault</brand>
<model>Duster</model>
<brand>Renault</brand>
</person>
Of course I could convert the XML to a String and remove the tags with replaceAll or something like this, but I was wondering if there is a nicer way to achieve this.
If you need to generate this output, you can use JAXB as follows:
1) Create a new Person class:
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementRefs;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"field"
})
#XmlRootElement(name = "person")
public class Person {
#XmlElementRefs({
#XmlElementRef(name = "name", type = JAXBElement.class, required = false),
#XmlElementRef(name = "model", type = JAXBElement.class, required = false),
#XmlElementRef(name = "brand", type = JAXBElement.class, required = false)
})
protected List<JAXBElement<String>> field;
public List<JAXBElement<String>> getNameOrModelOrBrand() {
if (field == null) {
field = new ArrayList<>();
}
return this.field;
}
}
2) Create an ObjectFactory to make it easier to use the person class:
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private final static QName _PersonName_QNAME = new QName("", "name");
private final static QName _PersonModel_QNAME = new QName("", "model");
private final static QName _PersonBrand_QNAME = new QName("", "brand");
public ObjectFactory() {
}
public Person createPerson() {
return new Person();
}
#XmlElementDecl(namespace = "", name = "name", scope = Person.class)
public JAXBElement<String> createPersonName(String value) {
return new JAXBElement<>(_PersonName_QNAME, String.class, Person.class, value);
}
#XmlElementDecl(namespace = "", name = "model", scope = Person.class)
public JAXBElement<String> createPersonModel(String value) {
return new JAXBElement<>(_PersonModel_QNAME, String.class, Person.class, value);
}
#XmlElementDecl(namespace = "", name = "brand", scope = Person.class)
public JAXBElement<String> createPersonBrand(String value) {
return new JAXBElement<>(_PersonBrand_QNAME, String.class, Person.class, value);
}
}
Use the factory as follows:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
import java.util.List;
...
ObjectFactory factory = new ObjectFactory();
Person person = factory.createPerson();
List<JAXBElement<String>> list = person.getNameOrModelOrBrand();
list.add(factory.createPersonName("Pedro"));
list.add(factory.createPersonModel("Logan"));
list.add(factory.createPersonBrand("Renault"));
list.add(factory.createPersonModel("Duster"));
list.add(factory.createPersonBrand("Renault"));
JAXBContext ctx = JAXBContext.newInstance(Person.class);
Marshaller marshaller = ctx.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
StringWriter writer = new StringWriter();
marshaller.marshal(person, writer);
System.out.println(writer.toString());
The end result is XML as follows:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
<name>Pedro</name>
<model>Logan</model>
<brand>Renault</brand>
<model>Duster</model>
<brand>Renault</brand>
</person>
Creating elements in this way is the only way I know to get the end result you need.
There are probably various things you could do to refactor the above code, to streamline the creation of the list of elements - but this shows you the basic approach.
As you already know - this is far from ideal. The end result is not any type of XML that I would want to receive.
Im struggling with the jaxb2marshaller configuration to work with classes generated from wsdl file.
My jaxb2marshaller
public Jaxb2Marshaller marshaller() {
final Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPaths("org.otheruri", "org.tempuri");
return marshaller;
}
I prepare a structure of classes and pass them thru marshaller.marshal to get the xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:ValidateResponse xmlns="http://otheruri.org" xmlns:ns2="http://tempuri.org/">
<ns2:ValidateResult>
<ArrayOfPerson>
<Person><NawNumber>personNAW</NawNumber></Person>
</ArrayOfPerson>
</ns2:ValidateResult>
</ns2:ValidateResponse>
But when I take this xml and run marshaller.unmarshall the ValidateResult.arrayOfPerson is null. There is no stacktrace or anything.
My generated classes look like this
package org.tempuri
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"validateResult"
})
#XmlRootElement(name = "ValidateResponse")
public class ValidateResponse {
#XmlElementRef(name = "ValidateResult", namespace = "http://tempuri.org/", type = JAXBElement.class, required = false)
protected JAXBElement<org.otheruri.ValidateResponse> validateResult;
public JAXBElement<org.otheruri.ValidateResponse> getValidateResult() {
return validateResult;
}
public void setValidateResult(JAXBElement<org.otheruri.ValidateResponse> value) {
this.validateResult = value;
}
}
and
package org.otheruri
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ValidateResponse", propOrder = {
"persons",
"userLoggedIn"
})
public class ValidateResponse {
#XmlElementRef(name = "Persons", namespace = "http://otheruri.org", type = JAXBElement.class, required = false)
protected JAXBElement<ArrayOfPerson> persons;
#XmlElementRef(name = "UserLoggedIn", namespace = "http://otheruri.org", type = JAXBElement.class, required = false)
protected JAXBElement<ValidateCredentialsResponse> userLoggedIn;
public JAXBElement<ArrayOfPerson> getPersons() {
return persons;
}
public void setPersons(JAXBElement<ArrayOfPerson> value) {
this.persons = value;
}
public JAXBElement<ValidateCredentialsResponse> getUserLoggedIn() {
return userLoggedIn;
}
public void setUserLoggedIn(JAXBElement<ValidateCredentialsResponse> value) {
this.userLoggedIn = value;
}
}
EDIT
So after time and lots of debbuging I know what is wrong. For unknown reason the created xml has ArrayOfPerson node (like class name) instead of Persons (that is specified in #XmlElementRef). Still I dont know why this is happening
So in the end it appeared that my generated object factory had 2 methods that returned same JaxbObject and accepted ArrayOfPerson object and very similar name, but had different name specified in the #XmlElementDecl, and of course I used the wrong one to generate xml, so I could not consume it later. :/
I wonder if someone can help me with a JAXB problem.
If I have an abstract class with 2 concrete implementations: For example (I have left out most of the markup/xml for brevity):
public abstract class Vehicle{}
public class Car extends Vehicle{}
public class Van extends Vehicle{}
Is there a way to have the xml below unmarshall correctly to the appropriate concrete class
<request>
<car>...</car>
</request>
rather than the following:
<request>
<vehicle xsi:type="car"></vehicle>
</request>
The reason I need this is to be backward compatible with our already published API.
Thanks in advance.
I have just answered in russian speaking community on similar question. Probably you looking for something like that:
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
Some quick example:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.io.StringReader;
import java.util.List;
public class Test {
public static void main(String... args) throws JAXBException {
String xmldata = "<request><car></car><van></van></request>";
StringReader reader = new StringReader(xmldata);
JAXBContext jaxbContext = JAXBContext.newInstance(Request.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Request request = (Request) unmarshaller.unmarshal(reader);
for (Vehicle object : request.getVehicles()) {
System.out.println(object.getClass());
}
}
}
#XmlRootElement(name = "request")
class Request {
private List<Vehicle> vehicles;
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public List<Vehicle> getVehicles() {
return vehicles;
}
public void setVehicles(List<Vehicle> vehicles) {
this.vehicles = vehicles;
}
}
abstract class Vehicle {
}
class Van extends Vehicle {
}
class Car extends Vehicle {
}
The output will be:
class Car
class Van
UPD:
Update after comment. For single entry it will work anyway just remove List:
#XmlRootElement(name = "request")
class Request {
private Vehicle vehicles;
#XmlElements({
#XmlElement(name = "car", type = Car.class),
#XmlElement(name = "van", type = Van.class)
})
public Vehicle getVehicles() {
return vehicles;
}
public void setVehicles(Vehicle vehicles) {
this.vehicles = vehicles;
}
}
Hope this will help.
You can use annotations and annotate the concrete implementations. In this case #XmlType() above Car or Van. This way you will keep your xml generic.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Item", propOrder = {
"code",
"name",
"price"
})
#XmlRootElement(name="inventory")
public class Item {
#XmlElement(name="catalog_num", required = true)
protected String code;
#XmlElement(name="catalog_descrip", required = true)
protected String name;
#XmlElement(name="prod_price")
protected double price;
public String getCode() {
return code;
}
JAXBContext databaseJC = JAXBContext.newInstance(Item.class);
Unmarshaller databaseUnmarshaller = databaseJC.createUnmarshaller();
File databaseXML = new File("src/forum6838882/database.xml");
Item item = (Item) databaseUnmarshaller.unmarshal(databaseXML);
My question is:
How could I get the #XmlElement(name="catalog_num", required = true) from item object. I need know the name="catalog_num" here.
JAXB (JSR-222) does not provide an API to introspect the metadata. You can however use the Java Reflection APIs (java.lang.reflect) to get the annotations and examine them yourself.
Demo
import java.lang.reflect.*;
import javax.xml.bind.annotation.*;
public class Demo {
public static void main(String[] args) throws Exception {
Field field = Item.class.getDeclaredField("code");
XmlElement xmlElement = field.getAnnotation(XmlElement.class);
System.out.println(xmlElement.name());
}
}
Output
catalog_num
In the root.class from my xi-schema, the element item and ohter objects are part of an itemList:
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
//...
protected List<Object> itemList;
I've in the ObjectFactory.class from the main-schema some items as JAXBElements like this:
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<numItemType> createDetailedInformation(numItemType num) {
return new JAXBElement<numItemType>(_detailedInformation_QNAME, numItemType.class, null, num);
}
So the numItemType has some attributes and value(num) for the JAXBElement.
NumItemType.class:
#XmlJavaTypeAdapter(numItemTypeAdapter.class)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
But when JAXB unmarshal the XML document, it will has only elements, for example:
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
When I marshal it, it should become (like the JAXB java code):
<detailedInformation element2="5678" element3="bla">1234</detailedInformation>
Therefore, I have written an numItemTypeAdapter.class with
NumItemTypeAdapter extends XmlAdapter
AdaptedNum.class:
public class AdaptedNum {
#XmlElement
private double element1;
#XmlElement
private String element2;
#XmlElement
private String element3;
/** Some getter/setter methods */
}
I thought, that would be help me http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html, but it is all a bit tricky :-/
It's a bit tricky to sort out exactly where your problem may be occurring. I'm assuming your original model was generated from an XML Schema, this should work as is without any modifications. I've attempted below to provide a scaled down version of your example which may help.
Root
package forum11343610;
import java.util.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElementRef(name = "item", namespace = "xi", type = JAXBElement.class, required = false)
protected List<Object> itemList = new ArrayList<Object>();
public List<Object> getItemList() {
return itemList;
}
public void setItemList(List<Object> itemList) {
this.itemList = itemList;
}
}
NumItemType
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
ObjectFactory
package forum11343610;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;
#XmlRegistry
public class ObjectFactory {
private static final QName _detailedInformation_QNAME = new QName("de-schema", "detailedInformation");
#XmlElementDecl(namespace = "xi", name = "item")
public JAXBElement<NumItemType> createItem(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
#XmlElementDecl(namespace = "de-schema", name = "detailedInformation", substitutionHeadNamespace = "xi", substitutionHeadName = "item")
public JAXBElement<NumItemType> createDetailedInformation(NumItemType num) {
return new JAXBElement<NumItemType>(_detailedInformation_QNAME, NumItemType.class, null, num);
}
}
Demo
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
ObjectFactory objectFactory = new ObjectFactory();
Root root = new Root();
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
Thank your for your comment.
That's the point:
Demo
NumItemType numItemType = new NumItemType();
numItemType.num = BigDecimal.TEN;
numItemType.decimals = "1";
numItemType.precision = "2";
root.getItemList().add(objectFactory.createDetailedInformation(numItemType));
It should unmarshal and map the XML automatically.
XML Input
<detailedInformation>
<element1>1234</element1>
<element2>5678</element2>
<element3>bla</element3>
</detailedInformation>
With the Code:
Demo
JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class);
Unmarshaller u = jc.createUnmarshaller();
File xml = new File("D:/", "test.xml");
Root root = (Root) u.unmarshal(xml);
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root xmlns:ns2="de-schema" xmlns:ns3="xi">
<ns2:detailedInformation precision="2" decimals="1">10</ns2:detailedInformation>
</root>
I could parse the XML Document with DOM to a tree and marhalling with JAXB...
Thank you!
In other words:
I want to set new elements to the NumItemType.class for the unmarshalling without change the schema java code.
NumItemType.class
package forum11343610;
import java.math.BigDecimal;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "numItemType", namespace = "xi", propOrder = {
"num"
})
#XmlJavaTypeAdapter(NumItemTypeAdapter.class)
public class NumItemType {
#XmlValue
protected BigDecimal num;
#XmlAttribute(name = "precision")
protected String precision;
#XmlAttribute(name = "decimals")
protected String decimals;
//... more Attributes
}
NumItemTypeAdapter.class
public class NumItemTypeAdapter extends XmlAdapter<AdaptedNum, NumItemType> {
#Override
public NumItemType unmarshal(AdaptedNum an) throws Exception {
NumItemType nit = new NumItemType();
nit.setNum(an.getNum);
nit.setPrecision(an.getPrecision);
nit.setDecimals(an.getDecimals)
return nit;
}
}
AdaptedNum.class
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "adaptedNum", namespace = "", propOrder = {
"element1",
"element2",
"element3"
})
public class AdaptedNum {
#XmlElement(name ="element1")
protected BigDecimal num;
#XmlElement(name ="element2")
private String decimals;
#XmlElement(name ="element3")
private String precison;
// set/get method
}