Mapping xml attributes to java attributes - java

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);

Related

Convert SOAP XML String to Java Object

I want to convert SOAP XML to Java Object without using xsd files and other stuff. I generate SOAP XML using POJO classes, #Xml annotations. What I want is to somehow deserialize String to not Envelope.class Object, but NumberToWords.class Object.
And by the way I didn't wanna have ns2:/xmlns="" stuff, that's why I used final static String as #XmlAttribute`s instead of namespace initializations.
Here are my classes >>>
#Setter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
#XmlRootElement(name = "soap:Envelope")
public class Envelope {
#XmlAttribute(
name = "xmlns:soap"
)
#JacksonXmlProperty(isAttribute = true)
private static final String xmlns = "http://schemas.xmlsoap.org/soap/envelope/";
#XmlElement(
name = "soap:Body"
)
private Body body;
public String printBody() {
return body.toString();
}
}
#Getter
#Setter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Body {
#XmlElement(name = "NumberToWords")
private NumberToWords numberToWords;
#XmlElement(name = "Name")
private Name name;
}
#Getter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
#XmlAccessorType(XmlAccessType.FIELD)
public class NumberToWords {
#XmlAttribute(name = "xmlns")
private final static String xmlns = "http://www.dataaccess.com/webservicesserver/";
#XmlElement(name = "ubiNum")
private Integer ubiNum;
}
Mapper Class which gets Marshaller Bean from another config class, but I decided to not add it here.
#Component
#RequiredArgsConstructor
public class EnvelopeMapper {
private final Marshaller marshaller;
#SneakyThrows
public String getXml(NumberToWords numberToWords) {
Envelope envelope = Envelope.builder()
.body(
Body.builder()
.numberToWords(numberToWords)
.build())
.build();
StringWriter stringWriter = new StringWriter();
marshaller.marshal(envelope, stringWriter);
return stringWriter.toString();
}
}
localhost:8080/get-num-xml/123
STRING:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<NumberToWords xmlns="http://www.dataaccess.com/webservicesserver/">
<ubiNum>123</ubiNum>
</NumberToWords>
</soap:Body>
</soap:Envelope>
When I call this endpoint it throws an exception.
#GetMapping("/get-num-xml/{num}")
public String getNumXml(#PathVariable Integer num) throws JAXBException {
String xml = envelopeService.getNumberToWords(num);
NumberToWords numberToWords = (NumberToWords) unmarshaller.unmarshal(new StringReader(xml));
System.out.println(numberToWords.getUbiNum());
return envelopeService.getNumberToWords(num);
}
GIVES:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Envelope"). Expected elements are <{}Name>,<{}body>,<{}soap:Envelope>
This one is also throws an exception.
#GetMapping("/get-num-xml/{num}")
public String getNumXml(#PathVariable Integer num) throws JAXBException, SOAPException, IOException {
String xml = envelopeService.getNumberToWords(num);
SOAPMessage message = MessageFactory.newInstance().createMessage(null, new ByteArrayInputStream(xml.getBytes()));
JAXBContext jc = JAXBContext.newInstance(NumberToWords.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
NumberToWords rc = (NumberToWords) unmarshaller.unmarshal(message.getSOAPBody().extractContentAsDocument());
return envelopeService.getNumberToWords(num);
}
GIVES:
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.dataaccess.com/webservicesserver/", local:"NumberToWords"). Expected elements are (none)
I don't have a clue how to unmarshal xml, so I hope someone has an answer for this one.

XML annotations (JAXB) suggestions for unmarshalling and marshalling

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>

JAXB: unmarshal diferent xml to same object

I have 3 input XML that have, pretty much, the same elements and attributes, in fact, they represent the same thing, so I want to marshall them to the same object, something like this:
Request One:
<?xml version="1.0" encoding="UTF-8"?>
<RequestOne>
<id>123</id>
<name>foo</name>
</RequestOne>
Request Two:
<?xml version="1.0" encoding="UTF-8"?>
<RequestTwo>
<id>123</id>
<value>val</value>
</RequestTwo>
Request Three:
<?xml version="1.0" encoding="UTF-8"?>
<RequestThree>
<name>foo</name>
<value>val</value>
</RequestThree>
Desired Object (something like):
#XmlRootElement
public class Resource{
#XmlElement
private String id;
#XmlElement
private String name;
#XmlElement
private String value;
//(...) more code
}
But I can't use multiple RootElement annotations to ask JAXB to unmarshall all of the 3 request to objects of the class Resource
Is there a way to do it? Or I must make the 3 sepparated classes?
Thanks for your help
Option 1
Unmarshal using the overloaded Generic unmarshal method :
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(Base.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
JAXBElement<Base> basea = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootA><name>nanana</name></RootA>")), Base.class);
System.out.println(basea.getValue().getName());
JAXBElement<Base> baseb = unmarshaller.unmarshal(new StreamSource(new StringReader("<RootB><name>nbnbnb</name></RootB>")), Base.class);
System.out.println(baseb.getValue().getName());
}
Option 2
You can always use Java's class subtyping capabilites ? JAXB does annotation scanning on parent class as well. This example works
public static class Base {
private String name ;
#XmlElement(name = "name")
public String getName() {
return name;
}
public Base setName(String name) {
this.name = name;
return this;
}
}
#XmlRootElement( name = "RootA")
public static class RootA extends Base{
}
#XmlRootElement( name = "RootB")
public static class RootB extends Base {
}
public static void main (String [] args) throws JAXBException {
JAXBContext jaxbContext = JAXBContext.newInstance(RootA.class,RootB.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
RootA rootA = (RootA)unmarshaller.unmarshal(new StringReader("<RootA><name>nanana</name></RootA>"));
System.out.println(rootA.getName());
RootB rootB = (RootB)unmarshaller.unmarshal(new StringReader("<RootB><name>nbnbnb</name></RootB>"));
System.out.println(rootB.getName());
}

Write pojo class for this xml file

I'm getting null value when unmarshelling the xml file to java class. But the xml file as values corresponding to the its attributes. Is there any mistake in my pojo class or unmarshelling?
Please help
This is my pojo
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "")
#XmlRootElement(name = "CardUpdateResponse",namespace="http://www.samople.com/Prepaid")
public class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
//Setters and Getters
}
This is my xml file
<?xml version="1.0" encoding="UTF-8"?>
<CardUpdateResponse xmlns="http://www.samople.com/Prepaid">
<CARDUPDATE_RET>
<ResErrorMsg>ID Issue Date must be equal or less than present date</ResErrorMsg>
<ResErrorCode>ErrIsud01</ResErrorCode>
<ResCode>0</ResCode>
<ACCOUNTNUMBER>2000000003918246</ACCOUNTNUMBER>
</CARDUPDATE_RET>
</CardUpdateResponse>
this is code for unmarshelling
public class XmlUnmarshelling {
public void unmarshell()
{
try
{
System.out.println("xml unmarshelling class");
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(FVCardUpdateResponse.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
FVCardUpdateResponse CARDUPDATE_ret = (FVCardUpdateResponse) jaxbUnmarshaller.unmarshal(file);
System.out.println("xml unmarshelled = "+CARDUPDATE_ret.getResErrorMsg());//Getting null value as response.
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
Your POJO doesn't have the same structure as your XML. CardUpdateResponse doesn't directly contain the properties in your POJO, it contains CARDUPDATE_RET element which contains the properties.
You could modify your POJO like this to match the XML:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "CardUpdateResponse", namespace="http://www.samople.com/Prepaid")
public static class CardUpdateResponseWrapper {
#XmlElement(name="CARDUPDATE_RET")
private FVCardUpdateResponse response;
// Getter and setter for response
public static class FVCardUpdateResponse {
#XmlElement(name = "AccountNumber")
private String AccountNumber;
#XmlElement(name = "ResCode")
private String ResCode;
#XmlElement(name = "ResErrorCode")
private String ResErrorCode;
#XmlElement(name = "ResErrorMsg")
private String ResErrorMsg;
// Getters and setters
}
}
Now the CardUpdateResponseWrapper class will represent your root XML element and it will have instance of FVCardUpdateResponse which will represent the CARDUPDATE_RET XML element.
Do unmarshall it, just call:
File file = new File("D:/var/lib/tomcat7/webapps/tmpFiles/1.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(CardUpdateResponseWrapper.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
CardUpdateResponseWrapper wrapper = (CardUpdateResponseWrapper) jaxbUnmarshaller.unmarshal(file);
System.out.println(wrapper.getResponse().getResErrorMsg());
I think that the issue is a combination of two problems, one what Bohuslav is saying, the other you need to repeat your namespace on every XmlElement annotation, e.g.
#XmlElement(name = "ResCode", namespace="http://www.samople.com/Prepaid")
and one particular issue, you need to match the cases as well so the name for AccountNumber should be capitalized ACCOUNTNUMBER

jaxb unmarshalling with other class as property

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;

Categories

Resources