I found a lot of articles that describe how to unmarshal a sequence of XML elements to a HashMap as long as they are within a "parent" element. However, I do not get this to work with the children directly under the root element!
Option 1 - Works:
<?xml version="1.0" encoding="UTF-8"?>
<checks>
<checks>
<check key="check1"/>
<check key="check2"/>
...
</checks>
</checks>
Option 2 - Does not work:
<?xml version="1.0" encoding="UTF-8"?>
<checks>
<check key="check1"/>
<check key="check2"/>
...
</checks>
Checks:
package com.foo.conf;
import java.util.Map;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="checks")
public class Checks {
#XmlJavaTypeAdapter(ChecksAdapter.class)
#XmlElement(name="checks")
public Map<String, Check> checkMap;
}
Check:
package com.foo.conf;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlValue;
public class Check {
#XmlAttribute public String key;
#XmlValue public String description;
public Check() { }
public Check(String key) {
this.key = key;
}
public String getCheckKey() {
return this.key;
}
}
CheckMapType:
package com.foo.conf;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
class CheckMapType {
#XmlElement(name="check")
public List<Check> checkList; // = new ArrayList<Check>();
}
ChecksAdapter:
package com.foo.conf;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
final class ChecksAdapter extends XmlAdapter<CheckMapType, Map<String, Check>> {
#Override
public CheckMapType marshal(Map<String, Check> arg0) throws Exception {
return null;
}
#Override
public Map<String, Check> unmarshal(CheckMapType arg0) throws Exception {
System.out.println("u: " + arg0.checkList.size());
Map<String, Check> map = new HashMap<String, Check>();
for (Check check : arg0.checkList) {
System.out.println(check);
map.put(check.key, check);
}
return map;
}
}
This is (some dummy test lineS) how I generate the classes/invoke the unmarshalling:
JAXBContext jc = JAXBContext.newInstance(Checks.class);
Unmarshaller u = jc.createUnmarshaller();
Checks c = (Checks) u.unmarshal(new File("checks.xml"));
System.out.println(c.checkMap.size());
Any idea on how to get option #2 to work? It works when using a List instead of the Map but I need the HashMap as I have to access the objects by the given keys...
Any hints much appreciated!
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
JAXB will treat each object relationship with a nesting relationship. Map is treated like an Object instead of a Collection so this is why you are getting the behaviour that you are seeing.
MOXy has an XPath based mapping extension called #XmlPath that could be used for this use case.
package com.foo.conf;
import java.util.Map;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="checks")
public class Checks {
#XmlJavaTypeAdapter(ChecksAdapter.class)
#XmlPath(".")
public Map<String, Check> checkMap;
}
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
How are you generaing the JAXB classes? I am not sure what exactly are you trying to do but the below very simple code works for me ..
JAXBContext jc = JAXBContext.newInstance(ChecksType.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
ChecksType chksType = (ChecksType) unmarshaller.unmarshal(new File("/path/to/xml"));
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(chksType, System.out);
System.err.println(chksType.getCheck().get(0).getKey());
for (CheckType checkType : chksType.getCheck()) {
System.out.println("key = " + checkType.getKey() + ", " + checkType);
}
and here is my JAXB generated classes ..
ChecksType (Root element)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "checksType", propOrder = { "check" })
#XmlRootElement(name = "checks")
public class ChecksType {
#XmlElement(required = true)
protected List<CheckType> check;
public List<CheckType> getCheck() {
if (check == null) {
check = new ArrayList<CheckType>();
}
return this.check;
}
}
And checkType (the child)
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "checkType")
public class CheckType {
#XmlAttribute(name = "key")
protected String key;
public String getKey() {
return key;
}
public void setKey(String value) {
this.key = value;
}
}
Related
I am parsing a XML request using Java. The XML structure is like this:
<?xml version="1.0" encoding="UTF-8"?>
<TestServices>
<header>
//Header Details
</header>
<body>
<ele1>
<ele2>
<ele3>534159XXXXXX0176</ele3> //Or ele_3, ele03, ele_03
</ele2>
</ele1>
</body>
</TestServices>
I have created classes for the same to read the Header and Body elements. Each node is a class and I am reading the ele3 value like this.
String ele3 = testServicesRequest.getBody().getEle1().getEle2().getEle3();
The element name for ele3 can be different based on different request. I have used Generate Java class from xsd feature in eclipse and it has generated classes like this.
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"ele3"
})
public class ReqEle2 {
#XmlElement(name = "ele3", required = true)
protected String ele3;
public String getEle3() {
return ele3;
}
public void setEle3(String value) {
this.ele3 = value;
}
}
My requirement is simple. I just want to put multiple element names for single getEle3() method. eg. ele_3, ele03, ele_03 using less code changes. Or Please suggest me any other efficient way to do that.
For now I am trying to do this like this which I think is not good.
public class ReqEle3 {
#XmlElement(name = "ele03", required = true)
protected String ele3_1="";
#XmlElement(name = "ele_3", required = true)
protected String ele3_2="";
#XmlElement(name = "ele3", required = true)
protected String ele3_3="";
#XmlElement(name = "ele3_old", required = true)
protected String ele3_4="";
public String getEle3() {
if(ele3_1 != null && !ele3_1.isEmpty()){
return ele3_1;
}
else if(ele3_2 != null && !ele3_2.isEmpty()){
return ele3_2;
}
else if(ele3_3 != null && !ele3_3.isEmpty()){
return ele3_3;
}
else if(ele3_4 != null && !ele3_4.isEmpty()){
return ele3_4;
}
return "";
}
}
You can write custom deserialiser for ele3 node. To be precise, custom deserialiser for ele2 node because this is the last constant node. Below example contains only required part to understand the solution:
import org.w3c.dom.Element;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.io.StringReader;
public class JaxbApp {
public static void main(String[] args) throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(ReqEle1.class);
String xml0 = "<ele1><ele2><ele3>534159XXXXXX0176</ele3></ele2></ele1>";
String xml1 = "<ele1><ele2><ele_3>534159XXXXXX0176</ele_3></ele2></ele1>";
String xml2 = "<ele1><ele2><ele03>534159XXXXXX0176</ele03></ele2></ele1>";
for (String xml : new String[]{xml0, xml1, xml2}) {
StringReader reader = new StringReader(xml);
Object unmarshal = jaxbContext.createUnmarshaller().unmarshal(reader);
System.out.println(unmarshal);
}
}
}
#XmlRootElement(name = "ele1")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {"ele2"})
class ReqEle1 {
#XmlJavaTypeAdapter(ReqEle2XmlAdapter.class)
#XmlElement(name = "ele2", required = true)
protected ReqEle2 ele2;
// getters, setters
}
class ReqEle2XmlAdapter extends XmlAdapter<Object, ReqEle2> {
#Override
public ReqEle2 unmarshal(Object v) {
Element element = (Element) v;
ReqEle2 reqEle2 = new ReqEle2();
reqEle2.setEle3(element.getFirstChild().getTextContent());
return reqEle2;
}
#Override
public Object marshal(ReqEle2 v) throws Exception {
return null; // Implement if needed
}
}
class ReqEle2 {
protected String ele3;
// getters, setters
}
Above code prints:
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
ReqEle1{ele2=ReqEle2{ele3='534159XXXXXX0176'}}
See also:
JAXB #XmlAdapter for arbitrary XML
Unbelievable how many questions there are already about this subject. Still, none of them helps me because what I understood is that it is a common error and I believe my code is correct because it is exactly the same as the one of another project my colleague passed me.
But.. I have this error
unable to marshal type "modules.CollaborationInfo" as an element because it is missing an #XmlRootElement annotation
Given this class CollaborationInfo:
package modules;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CollaborationInfo", propOrder = {
"AgreementRef",
"ConversationId"
})
public class CollaborationInfo {
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name="AgreementRef")
public static class AgreementRef {
#XmlAttribute
private String pmode;
public String getPmode() {
return pmode;
}
public void setPmode(String pmode) {
this.pmode = pmode;
}
}
#XmlElementRef(name = "AgreementRef")
protected AgreementRef AgreementRef = new AgreementRef();
#XmlElement(name="ConversationId")
protected String ConversationId;
public String getPmode() {
return AgreementRef.getPmode();
}
public void setPmode(String value) {
this.AgreementRef.setPmode(value);
}
public String getConversationId() {
return ConversationId;
}
public void setConversationId(String value) {
this.ConversationId = value;
}
}
And as main():
public static void main(String[] args) throws Exception{
JAXBContext contextObj = JAXBContext.newInstance(CollaborationInfo.class);
Marshaller marshallerObj = contextObj.createMarshaller();
marshallerObj.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
CollaborationInfo CI = new CollaborationInfo();
CI.setPmode("String1");
CI.setConversationId("String2");
marshallerObj.marshal(CI, new FileOutputStream("C:\\OUT.xml"));
}
I should be able to have as output (OUT.xml):
<?xml version="1.0" encoding="UTF-8"?>
<CollaborationInfo>
<AgreementRef pmode="String1"/>
<ConversationId>String2</ConversationId>
</CollaborationInfo>
But I just can't.
Can someone tell me where I am wrong?
(Of course the real XML is much more long and complex, but probably if I'm able to have this part working I'm able to continue the rest)
As the errors says, you need to add the annotation #XmlRootElement in front of the class CollaborationInfo. Your #XmlRootElement is for the static class AgreementRef.
package modules;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElementRef;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CollaborationInfo", propOrder = {
"AgreementRef",
"ConversationId"
})
public class CollaborationInfo {
#XmlAccessorType(XmlAccessType.FIELD)
public static class AgreementRef {
#XmlAttribute
private String pmode;
public String getPmode() {
return pmode;
}
public void setPmode(String pmode) {
this.pmode = pmode;
}
}
#XmlElement(name = "AgreementRef")
protected AgreementRef AgreementRef = new AgreementRef();
#XmlElement(name="ConversationId")
protected String ConversationId;
public String getPmode() {
return AgreementRef.getPmode();
}
public void setPmode(String value) {
this.AgreementRef.setPmode(value);
}
public String getConversationId() {
return ConversationId;
}
public void setConversationId(String value) {
this.ConversationId = value;
}
}
This is old but just in case someone runs into it, it was answered here:
https://stackoverflow.com/a/59249216/5835746
Basically, you should not use the auto-generated classes directly but use the methods from ObjectFactory, which was also auto-generated by the plugin, to create instances of those classes.
Editing the auto-generated classes, to add the #XmlRootElement annotation is probably not a good idea.
I am getting below exception, i need some help to resolve the issue.
If remove the namespace in the object factory and with out package-info.java class it is working fine.
Exception that is throwing now
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
There's no ObjectFactory with an #XmlElementDecl for the element {}shipping.
this problem is related to the following location:
at protected javax.xml.bind.JAXBElement com.jverstry.annotations.generics.Market$Detail.shipping
at com.jverstry.annotations.generics.Market$Detail
at protected com.jverstry.annotations.generics.Market$Detail com.jverstry.annotations.generics.Market.detail
at com.jverstry.annotations.generics.Market
ObjectFactory class which is creating the jaxbelement
package com.jverstry.annotations.generics;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
import org.example.customer.Customer;
#XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Market.Detail.Shipping createShipping() {
return new Market.Detail.Shipping();
}
private final static QName _Shipping_QNAME = new QName("http://www.example.org/customer", "shipping");
#XmlElementDecl(namespace = "http://www.example.org/customer", name = "shipping")
public JAXBElement<Market.Detail.Shipping> createShipping(Market.Detail.Shipping value) {
return new JAXBElement<Market.Detail.Shipping>(_Shipping_QNAME, Market.Detail.Shipping.class, value);
}
}
Class package-info.java, where the name spaces are mentioned for the response xml
#XmlSchema(namespace = "http://www.example.org/customer", elementFormDefault = XmlNsForm.QUALIFIED)
package com.jverstry.annotations.generics;
import javax.xml.bind.annotation.*;
Demo class where marshalling object
package com.jverstry.annotations.generics;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Market.class);
Market market = new Market();
Market.Detail md = new Market.Detail();
Market.Detail.Shipping mds = new Market.Detail.Shipping();
mds.setAvailable(false);
JAXBElement<Market.Detail.Shipping> shipping = new ObjectFactory().createShipping(mds);
shipping.setNil(true);
md.setShipping(shipping);
market.setDetail(md);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(market, System.out);
}
}
Market class, this is the main root class where jaxbcontext is created
package com.jverstry.annotations.generics;
import java.math.BigDecimal;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "detail" })
#XmlRootElement(name = "Market")
public class Market
{
#XmlElement(required = false)
protected Market.Detail detail;
public Market.Detail getDetail() {
return detail;
}
public void setDetail(Market.Detail detail) {
this.detail = detail;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "shipping" })
public static class Detail
{
#XmlElementRef(name = "shipping")
protected JAXBElement<Market.Detail.Shipping> shipping;
public JAXBElement<Market.Detail.Shipping> getShipping() {
return shipping;
}
public void setShipping(JAXBElement<Market.Detail.Shipping> value) {
this.shipping = value;
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = { "value" })
public static class Shipping
{
#XmlValue
protected BigDecimal value;
#XmlAttribute(name = "available")
protected Boolean available;
public BigDecimal getValue() {
return value;
}
public void setValue(BigDecimal value) {
this.value = value;
}
public Boolean getAvailable() {
return available;
}
public void setAvailable(Boolean value) {
this.available = value;
}
}
}
}
You need to create the JAXBContext by passing in the ObjectFactory class or the package name of the generated model to ensure the ObjectFactory class is processed.
If you specify the namespace property on the #XmlElementRef annotation things should work.
We are using Spring for a project, and I need to marshall and unmarshall lists of objects
from and to XML.
The business objects themselves do not have #XmlRootElement annotations on them for various reasons, which cannot change.
I can do this easily with javax.xml by using a wrapper class. I'm using a simple testObject with a numeric ID and a description field:
/**
* testObject for xml convert
*
*/
package xmlconvert;
import java.io.File;
import java.io.IOException;
import java.util.List;
//import javax.xml.bind.JAXBContext;
//import javax.xml.bind.JAXBException;
//import javax.xml.bind.Marshaller;
//import javax.xml.bind.Unmarshaller;
//import javax.xml.bind.annotation.XmlRootElement;
//import javax.xml.bind.annotation.XmlType;
public class testObject
{
private static final long serialVersionUID = 1L;
public testObject()
{
setObjectid( 0L );
setDescription( " " );
}
public testObject( Long oid, String desc )
{
setObjectid( oid );
setDescription( desc );
}
private Long objectid;
public Long getObjectid()
{
return this.objectid;
}
public void setObjectid( Long oid )
{
this.objectid = oid;
}
private String description;
public String getDescription()
{
return this.description;
}
public void setDescription( String d )
{
this.description = d;
}
}
My wrapper class looks like this:
/**
* JAXBToList
*
* An object that holds the list of objects to convert.
*
*/
package xmlconvert;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import xmlconvert.testObject;
#XmlRootElement(name="testObjects")
public class JAXBToList
{
protected List<testObject> list;
public JAXBToList()
{
list = new ArrayList<testObject>();
}
public JAXBToList( List<testObject> list )
{
this.list = list;
}
#XmlElement(name="testObject")
public List<testObject> getList()
{
return (List<testObject>) this.list;
}
}
When I convert an ArrayList of these objects to XML, the code looks like this:
#XmlElements({
#XmlElement(name="testObject",
type=testObject.class)
})
public static void convertListToXML( JAXBToList objectList,
String xmlFilePath )
throws IOException, JAXBException {
JAXBContext jc = JAXBContext.newInstance( JAXBToList.class );
marshaller = jc.createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
marshaller.marshal( objectList,
new StreamResult( new FileOutputStream(xmlFilePath) ) );
}
I would like to use Spring marshaller and unmarshaller to do this, but there seems to be no way to set the context to use the JAXBToList class in the oxm class Jaxb2Marshaller, or the other varieties of marshallers.
I know there is a set Jaxb Context Properties method, but there are no instructions on what I would need to set, or if using this would do the same thing as setting the jaxb Context as shown above.
Does anyone have a link that illustrates how oxm may be used for a list of objects? A google search does not turn up much.
I have a String property in an object annotated as follows:
#XmlElement(name = "Item", required = true, nillable = true)
private String item;
The result after marshaling is
<Item xsi:nil="true"/>
while I would like it to be
<Item/>
since the third-party service accepting my XML messages wants it like the latter case. I am using jaxb2. Does anyone knows how I could possibly do this?
Thanks a lot
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The following example requires the use of MOXy as the JAXB provider. This is because the JAXB RI does not call the XmlAdapter when the field/property is null. For information on specifying MOXy as your JAXB provider see:
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
StringAdapter
The XmlAdapter will convert the String value to an object with a property annotated with #XmlValue.
package forum8986842;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String>{
#Override
public String unmarshal(AdaptedString adaptedString) throws Exception {
if(null == adaptedString) {
return null;
}
String string = adaptedString.value;
if("".equals(string)) {
return null;
}
return string;
}
#Override
public AdaptedString marshal(String string) throws Exception {
AdaptedString adaptedString = new AdaptedString();
adaptedString.value = string;
return adaptedString;
}
public static class AdaptedString {
#XmlValue public String value;
}
}
Root
The #XmlJavaTypeAdapter annotation is used to specify the XmlAdapter:
package forum8986842;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Root")
public class Root {
private String item;
#XmlElement(name = "Item", required = true, nillable = true)
#XmlJavaTypeAdapter(StringAdapter.class)
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
}
Demo
The following code can be used to demonstrate the above mapping. Two documents are used one with an empty Item element, and the other with a populated Item element.
package forum8986842;
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
private JAXBContext jc;
public Demo() throws JAXBException {
jc = JAXBContext.newInstance(Root.class);
}
public static void main(String[] args) throws Exception {
Demo demo = new Demo();
demo.demo("<Root><Item/></Root>");
demo.demo("<Root><Item>Hello World</Item></Root>");
}
private void demo(String xml) throws JAXBException {
System.out.println("\n\nINPUT: " + xml);
StringReader stringReader = new StringReader(xml);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(stringReader);
System.out.println("ITEM: " + root.getItem());
System.out.print("OUTPUT: ");
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
marshaller.marshal(root, System.out);
}
}
Output
The following is the output from running the demo code:
INPUT: <Root><Item/></Root>
ITEM: null
OUTPUT: <Root><Item/></Root>
INPUT: <Root><Item>Hello World</Item></Root>
ITEM: Hello World
OUTPUT: <Root><Item>Hello World</Item></Root>
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
http://blog.bdoughan.com/search/label/XmlAdapter
I found changing the xsd was easier
<xs:element name="name">
<xs:complexType/>
</xs:element>
and in your code, when you auto generate your java src/classes
you would specify new Name and set the Name to whichever object name belongs under