I have this two model.
AssetMetadata:
#XmlRootElement(name="AssetMetadata")
public class AssetMetadata {
private AssetMetadataType assetMetadataType;
private String id;
private String assetId;
....
AssetMetadataType:
#XmlRootElement(name = "AssetMetadataType")
public class AssetMetadataType {
private String id;
private String name;
....
I use the JaxB unmarshaller like this.
spring config:
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="ch.srf.esb.radioimporter.domain.AssetMetadata"/>
<oxm:class-to-be-bound name="ch.srf.esb.radioimporter.domain.AssetMetadataType"/>
</oxm:jaxb2-marshaller>
Java code:
#Autowired #Qualifier("marshaller") private Unmarshaller unmarshaller;
...
final InputStream is = new ByteArrayInputStream(xml.getBytes());
this.unmarshaller.unmarshal(new StreamSource(is));
Now when I send the following XML, the AssetMetadataType is not set:
<AssetMetadata>
<AssetMetadataType>
<id>1</id>
<name>EPG</name>
</AssetMetadataType>
<assetId>39b4864d-931b-40c6-85ad-c45251b97952</assetId>
<title>title</title>
<description>description</description>
</AssetMetadata>
What do I do wrong ?
#XmlRootElement should only be set on the root element. That's why it's called #XmlRootElement. It'll be ignored anywhere else.
Try removing #XmlRootElement from the AssetMetadataType class, and change the property in AssetMetadata to be:
#XmlElement(name="AssetMetadataType")
private AssetMetadataType assetMetadataType;
Related
Is there a way to map xml attributes to Java attributes using Jackson Faster xml.
Sample xml
<student>
<details>
<element key="firstName" value="John" />
<element key="lastName" value="Doe" />
</details>
</student>
The above xml needs to be mapped to below Java class.
public class Student {
private String firstName;
private String lastName;
}
for such cases, I have written a static piece of code:
public static <T> T unmarshall(String xml, Class<T> clazz) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(clazz);
Unmarshaller unmarshaller = jc.createUnmarshaller();
T obj = clazz.cast(unmarshaller.unmarshal(new StringReader(xml)));
return obj;
}
Also annotate your DTO with:
#Getter
#Setter
#XmlRootElement(name = "<SomeName>")
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
private String firstName;
private String lastName;
}
After you have this method, you can use it to serialize your data:
Student studentResponse = (Student) unmarshall(response, Student.class);
I have a sample XML as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<Education>
<School name="ABC" location="Mangalore">
<Student gender="M" housepincode="575020">
<FirstName>VJ</FirstName>
<LastName>K</LastName>
</Student>
<Student gender="M" housepincode="575002">
<FirstName>S</FirstName>
<LastName>K</LastName>
</Student>
</School>
</Education>
This has been unmarshalled with the help of JAXB using the following approach.
I have classes for Education,School and Student.
Education.java
import lombok.Data;
#Data
#XmlRootElement(name = "Education")
public class Education {
public School School;
}
School.java
import lombok.Data;
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class School {
#XmlAttribute
private String name;
#XmlAttribute
private String location;
#XmlElement(name ="Student")
private List<Student> Student;
}
Student.java
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
#XmlAttribute
private String gender;
#XmlAttribute
private String housepincode;
#XmlElement(name = "FirstName")
private String FirstName;
#XmlElement(name = "LastName")
private String LastName;
}
I have some concerns over the output I receive. First of all there are some warnings. I believe it is because i have missed some annotations which should be present. Any suggestions or guidance here please?
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.sun.xml.bind.v2.runtime.reflect.opt.Injector (file:/C:/Users/R.Premsagar/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.0-b170127.1453/jaxb-runtime-2.3.0-b170127.1453.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
WARNING: Please consider reporting this to the maintainers of com.sun.xml.bind.v2.runtime.reflect.opt.Injector
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
Education(School=School(name=ABC, location=Mangalore, Student=[Student(gender=M, housepincode=575020, FirstName=VJ, LastName=K), Student(gender=M, housepincode=575002, FirstName=S, LastName=K)]))
Also, the heirarchy of output does not seem correct to me, Eg: School = School tag?
I want to print this output to a separate XML to verify the result.
However the script fails
Main program:
List<Education> Entries = new ArrayList<Education>();
try {
File xmlFile = new File("Withattributes.xml");
JAXBContext jaxbContext;
jaxbContext = JAXBContext.newInstance(Education.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Education entries = (Education) jaxbUnmarshaller.unmarshal(xmlFile);
Entries.add(entries); //storing objects in a list for later use
//--------------------------------------------------------------
JAXBContext jaxbContext_w = JAXBContext.newInstance(Education.class);
Marshaller jaxbMarshaller = jaxbContext_w.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
jaxbMarshaller.marshal(jaxbContext_w, System.out);
}
catch (JAXBException e)
{
e.printStackTrace();
} catch (FactoryConfigurationError e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
I get the following error message:
Neither the class com.sun.xml.bind.v2.runtime.JAXBContextImpl nor one of the associated superclasses is known to this context.
at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:593)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:482)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:328)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:256)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:110)
at JaxbExample.main(JaxbExample.java:39)
Please let me know what could be done here..
UPDATE:
If the XML is updated to have a structure as below:
<?xml version="1.0" encoding="UTF-8"?>
<Education>
<School name="ABC" location="Mangalore">
<Student gender="M" housepincode="575020">
<FirstName>VJ</FirstName>
<LastName>K</LastName>
<Hobbies>
<Hobby time = "M">Novels</Hobby>
<Hobby time = "A">Gaming</Hobby>
</Hobbies>
</Student>
</School>
</Education>
Now the class of Student.java would change to
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
#XmlAttribute
private String gender;
#XmlAttribute
private String housepincode;
#XmlElement(name = "FirstName")
private String FirstName;
#XmlElement(name = "LastName")
private String LastName;
#XmlElementWrapper(name ="Hobbies")
#XmlElement(name="Hobby")
private List<Hobby> Hobbies;
}
and the Hobby.java
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Hobby{
#XmlAttribute(name="Time")
private String Time;
#XmlElement
private String Hobby;
}
With this modification I do not get the value of the Hobby tag. I get the list of Hobbies and values something like this:
Hobbies>
<Hobby time = "M"/>
<Hobby time = "A"/>
</Hobbies>
Could you correct me here please?
First issue:
I am no expert on JAXB so I do not have much idea on what could cause the warnings specified here but I tried following the code and it seems to work fine for me without any warnings. If you want to get more understanding then the best thing to do is search about it and read the doc to get more information. But my guess is that it's happening due to your second issue.
Also, I am using Moxy which is an extension of JAXB developed by Eclipse-link. It has many additional benefits compared to simple JAXB
Second issue:
obvious error is in this line:
jaxbMarshaller.marshal(jaxbContext_w, System.out);
why are you providing jaxbContext_w as an input to it. It should be the unmarshalled values. In your case, it is entries.
Following is the complete code:
Education:
#Data
#XmlRootElement(name = "Education")
#XmlAccessorType(XmlAccessType.FIELD)
public class Education {
public School School;
}
School:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class School {
#XmlAttribute
private String name;
#XmlAttribute
private String location;
#XmlElement
private List<Student> Student;
}
Student:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Student {
#XmlAttribute
private String gender;
#XmlAttribute
private String housepincode;
#XmlElement
private String FirstName;
#XmlElement
private String LastName;
}
Main:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("students.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Education.class).createUnmarshaller();
final Education education = unmarshaller.unmarshal(xmlStreamReader, Education.class).getValue();
System.out.println(education.toString());
Marshaller marshaller = JAXBContext.newInstance(Education.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(education, System.out);
}
}
Output (Without any error or warnings):
Education(School=School(name=ABC, location=Mangalore, Student=[Student(gender=M, housepincode=575020, FirstName=VJ, LastName=K), Student(gender=M, housepincode=575002, FirstName=S, LastName=K)]))
<Education>
<School name="ABC" location="Mangalore">
<Student gender="M" housepincode="575020">
<FirstName>VJ</FirstName>
<LastName>K</LastName>
</Student>
<Student gender="M" housepincode="575002">
<FirstName>S</FirstName>
<LastName>K</LastName>
</Student>
</School>
</Education>
TL;DR: When I unmarshall from XML to POJO I only have the XmlAttributes well mapped, however all XmlElement are null.
Hi there!
I have the following problem. This class was generated with JAXB
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"activity"
})
#XmlRootElement(name = "activityDetails", namespace = "http://lorem.ipsum.com/")
public class ActivityDetails {
#XmlElement(required = true)
protected Activity activity;
#XmlAttribute(name = "schemaVersion", required = true)
protected float schemaVersion;
#XmlAttribute(name = "actionType")
protected ActionTypes actionType;
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar timestamp;
This is an example XML
<activityDetails
actionType="CREATE"
schemaVersion="2.0"
timestamp="2020-01-02T15:31:50.549Z"
xmlns="http://lorem.ipsum.com/">
<activity>
<activityId>
<start>2020-01-01T03:00:00Z</start>
<end>2020-01-02T02:59:00Z</end>
</activityId>
</activity>
</activityDetails>
But, when this code is executed (please don't judge me, it's legacy code):
Object xmlClass = Class.forName("com.lorem.ipsum." + className).getConstructor().newInstance();
final JAXBContext jaxbContext = JAXBContext.newInstance(xmlClass.getClass());
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Object object = unmarshaller.unmarshal(new StringReader(element));
The result "object" have all XmlAttribute well mapped, but no one of their XmlElement
PS: The namespace in the generated class was added manually, if I don't do that I have this exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://lorem.ipsum.com/", local:"activityDetails"). Expected elements are <{}activityDetails>
Thanks in advance.
UPDATED: If I set all #XmlElement the namespace property I finally map the elements, but I must intervene all the classes. Is there another way to achieve that without having to modify all the fields of all the classes?
I guess I am able to figure out the issue. This is happening because you have not provided any prefix to your namespace in XML. Following code would work for your provided sample XML:
XML:
<activityDetails
actionType="CREATE"
schemaVersion="2.0"
timestamp="2020-01-02T15:31:50.549Z"
xmlns:ns0="http://lorem.ipsum.com/">
<activity>
<activityId>
<start>2020-01-01T03:00:00Z</start>
<end>2020-01-02T02:59:00Z</end>
</activityId>
</activity>
</activityDetails>
ActivityDetails.class:
#XmlAccessorType(XmlAccessType.FIELD)
#Data
#XmlRootElement(name = "activityDetails", namespace = "http://lorem.ipsum.com/")
public class ActivityDetails {
private Activity activity;
#XmlAttribute
private float schemaVersion;
#XmlAttribute
private String actionType;
#XmlAttribute
private String timestamp;
}
Activity.class:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Activity {
private ActivityID activityId;
}
ActivityID.class:
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class ActivityID {
private String start;
private String end;
}
Main.class:
public class Main {
public static void main(String[] args) throws JAXBException, XMLStreamException {
final InputStream inputStream = Main.class.getClassLoader().getResourceAsStream("activity.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(ActivityDetails.class).createUnmarshaller();
final ActivityDetails activityDetails = unmarshaller.unmarshal(xmlStreamReader, ActivityDetails.class).getValue();
System.out.println(activityDetails.toString());
Marshaller marshaller = JAXBContext.newInstance(ActivityDetails.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(activityDetails, System.out);
}
}
Following is your output:
ActivityDetails(activity=Activity(activityId=ActivityID(start=2020-01-01T03:00:00Z, end=2020-01-02T02:59:00Z)), schemaVersion=2.0, actionType=CREATE, timestamp=2020-01-02T15:31:50.549Z)
<ns0:activityDetails xmlns:ns0="http://lorem.ipsum.com/" schemaVersion="2.0" actionType="CREATE" timestamp="2020-01-02T15:31:50.549Z">
<activity>
<activityId>
<start>2020-01-01T03:00:00Z</start>
<end>2020-01-02T02:59:00Z</end>
</activityId>
</activity>
</ns0:activityDetails>
Finally I found this solution: Instead of putting the namespace in each of the XMLElements I put the following package-info.java in the package of the generated classes.
#XmlSchema(
elementFormDefault=XmlNsForm.QUALIFIED,
namespace="http://lorem.ipsum.com/",
xmlns={#XmlNs(prefix="", namespaceURI="http://lorem.ipsum.com/")})
package com.lorem.ipsum.generated;
import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
I have just started to learn how to make a java object from a XML. However, now I have a tricky input and I don't really know how to solve it.
This is the input:
<?xml version="1.0" encoding="UTF-8"?>
<return>
<productChange cd_product="711" ds_product="MBL40337 I151 BLACK P" cd_species="UN" cd_tipi="61062000" cd_cst="5" qt_weight="" cd_nbm="" dt="2016-05-04 11:47:36">
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="1" vl_product="61.39">
<tipovaluePrd tp_value="P" cd_value="1" ds_value="SALE" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="4" vl_product="129.8">
<tipovaluePrd tp_value="P" cd_value="4" ds_value="SALE STORE" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="5" vl_product="64.9">
<tipovaluePrd tp_value="P" cd_value="5" ds_value="SALE AT" cd_corrency="1" />
</productvalue>
<productvalue cd_company="1" cd_product="711" tp_value="P" cd_value="8" vl_product="122.78">
<tipovaluePrd tp_value="P" cd_value="8" ds_value="SALE FQ" cd_corrency="1" />
</productvalue>
</productChange>
</return>
To consume this xml with JaxB (XML provided by a third party company) I have built the following classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductUpdateResponse {
#XmlAttribute(name = "cd_product")
private Integer productCode;
#XmlAttribute(name = "ds_product")
private String productDescription;
#XmlAttribute(name = "cd_species")
private String productSpecie;
#XmlAttribute(name = "cd_tipi")
private Integer productTIPI;
#XmlAttribute(name = "cd_cst")
private Integer productCST;
#XmlAttribute(name = "qt_weight")
private String productWeight;
#XmlAttribute(name = "cd_nbm")
private String productNBM;
#XmlAttribute(name = "dt")
private String productDate;
#XmlElement(name = "productvalue")
private ProductValueType productValue;
// getters and setters
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductValue {
#XmlAttribute(name = "cd_product")
private Integer productCode;
#XmlAttribute(name = "cd_company")
private Integer companyCode;
#XmlAttribute(name = "tp_value")
private String valueType;
#XmlAttribute(name = "cd_value")
private Integer valueCode;
#XmlAttribute(name = "vl_product")
private BigDecimal productValue;
#XmlElement(name = "tipovaluePrd")
private ProductValueType productValueType;
//getters and seters
}
#XmlAccessorType(XmlAccessType.FIELD)
public class ProductValueType {
#XmlAttribute(name = "tp_value")
private String valueType;
#XmlAttribute(name = "cd_value")
private String valueCode;
#XmlAttribute(name = "ds_value")
private String valueDescription;
#XmlAttribute(name = "cd_corrency")
private Integer valueCurrency;
//getters and seters
}
However, the main information, for me, is comming as null (productvalue) I wonder if it is because there are four tags productvalue in the xml (I just need the one whose tipovaluePrd.cd_value is 4 ).
Below is the method I'm using to parse the XML:
private ProductUpdateResponse buildResponse(String rawResponse, Class<T> responseClass) {
JAXBContext jaxbContext = null;
try {
jaxbContext = JAXBContext.newInstance(responseClass);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader(rawResponse);
return (ProductUpdateResponse) unmarshaller.unmarshal(reader);
} catch (JAXBException | ClassCastException e) {
return null;
}
}
So, I wonder if there is a way to read specifically the value I want (productvalue element that has tipovaluePrd.cd_value = 4) and how could I do that. If there isn't, how should I build my class so I can read properly everything?
Changing the input is not an option here...
Thanks in advance for any help.
I'm actually surprised you're getting a result from JAXB with your current setup.
You lack a class representing your root element :
#XmlRootElement(name="return")
#XmlAccessorType(XmlAccessType.FIELD)
public class Return {
#XmlElement(name="productchange")
private ProductUpdateResponse response;
//getters and setters
}
Also, in your ProductUpdateResponse class, you should change :
#XmlElement(name = "productvalue")
private ProductValueType productValue;
by
#XmlElement(name = "productvalue")
private List<ProductValue> productValues;
Finally, in your buildResponse method, make sure the responseClass argument is Return.class.
With this, you should have a list with all your productvalue element. You just have to get the productvalue your interested in from it.
If you only want to get the element you're interested with :
As swasa suggested it, you can use the javax.xml.xpath to make an Xpath request on your XML.
If you're willing to change your JAXB Implementation : EclipseLink JAXB Implementation implements an #XmlPath annotation that allows you to bind objects according to an XPath request :
#XmlPath(name = "productvalue/tipovaluePrd[#cd_value=4]")
private ProductValueType productValueType;
Consider the following xml:
<Config>
<Paths>
<Path reference="WS_License"/>
</Paths>
<Steps>
<Step id="WS_License" title="License Agreement" />
</Steps>
</Config>
The following JAXB classes:
public class Path {
private String _reference;
public String getReference() {
return _reference;
}
#XmlAttribute
public void setReference( String reference ) {
_reference = reference;
}
}
And
public class Step {
private String _id;
private String _title;
public String getId() {
return _id;
}
#XmlAttribute
public void setId( String id ) {
_id = id;
}
public String getTitle() {
return _title;
}
#XmlAttribute
public void setTitle( String title ) {
_title = title;
}
}
Instead of storing the reference in the Path object as String, I'd like to hold it as a Step object. The link between those objects is the reference and id attributes. Is the #XMLJavaTypeAdapter attribute the way to go? Could anyone be so kind to provide an example of the correct usage?
Thanks!
EDIT:
I'd also would like to do the same technique with an element.
Consider the following xml:
<Config>
<Step id="WS_License" title="License Agreement">
<DetailPanelReference reference="DP_License" />
</Step>
<DetailPanels>
<DetalPanel id="DP_License" title="License Agreement" />
</DetailPanels>
</Config>
The following JAXB classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
#XmlIDREF
#XmlElement(name="DetailPanelReference", type=DetailPanel.class)
private DetailPanel[] _detailPanels; //Doesn't seem to work
}
#XmlAccessorType(XmlAccessType.FIELD)
public class DetailPanel {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
}
The property _detailPanels in the Step-object is empty and the link doesn't seems to work. Is there any option to create a link without creating a new JAXB object holding only the reference to the DetailPanel?
Thanks again : )!
You can use #XmlID to map a property as the key and #XmlIDREF to map the reference to the key for this use case.
Step
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String _id;
}
Path
#XmlAccessorType(XmlAccessType.FIELD)
public class Path {
#XmlIDREF
#XmlAttribute
private Step _reference;
}
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
UPDATE
Thanks! I Completely missed your article. I've extended my question,
do you have any clue if this is possible too? I do not want to create
a class with only holding the reference, I'd like to store it inside
the step class.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you are using MOXy as your JAXB (JSR-222) provider then you could leverage the #XmlPath annotation for your use case.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String id;
#XmlPath("DetailPanelReference/#reference")
#XmlIDREF
// private List<DetailPanel> _detailPanels; // WORKS
private DetailPanel[] _detailPanels; // See bug: http://bugs.eclipse.org/399293
}
For More Information
http://bugs.eclipse.org/399293
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html