using one POJO class for different xml responses - java

When I call rest service I get different xml responses, with different xml root element. I would like to know, are there any opportunities to unmarshal these xmls to one pojo class.
For example, I have a class RecordingCreated.
#XmlRootElement(name = "recordingCreated")
public class RecordingCreated {
private String nodeID;
private String cameraID;
private String recPath;
private String recordingStatus;
public String getNodeID() {
return nodeID;
}
#XmlElement
public void setNodeID(String nodeID) {
this.nodeID = nodeID;
}
public String getCameraID() {
return cameraID;
}
#XmlElement
public void setCameraID(String cameraID) {
this.cameraID = cameraID;
}
public String getRecPath() {
return recPath;
}
#XmlElement
public void setRecPath(String recPath) {
this.recPath = recPath;
}
public String getRecordingStatus() {
return recordingStatus;
}
#XmlElement
public void setRecordingStatus(String recordingStatus) {
this.recordingStatus = recordingStatus;
}
}
After calling rest service I can get xml response in the form of
<recordingCreated>
<nodeID>"111</nodeID>
<cameraID>222</cameraID>\
<recordingID>333</recordingID>\
<recPath>rec</recPath>
<recordingStatus>recorded</recordingStatus>
</recordingCreated>
And in the form of
<error>
<code>444</code>
<description>broker: access denied</description>
</error>
When I got first xml resposne, JAXB unmarshal good
JAXBContext jaxbContext = JAXBContext.newInstance(RecordingCreated.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
RecordingCreated recordingCreated = (RecordingCreated) jaxbUnmarshaller.unmarshal(inputStream);
But when I got second response, of course, I got an error, like this
javax.xml.bind.UnmarshalException: unexpected element (uri:"",
local:"error"). Expected elements are <{}recordingCreated>]]
Question: Is there any opportunity having one class unmarshal two various xml responses with different root elements?

Try creating two different sub classes for your two different responses with their corresponding roots.
You can have the current class as posted as the parent for both of them and depending on the response you would get, call the required class.

Related

Apache Camel sending marshalled data

I'm trying to send marshalled data through a Camel route. I've checked a few tutorials but can't get it to work and receive a RuntimeException.
What I want to accomplish:
Extract the body of the message.
Create a POJO with content and title fields.
Marshall the POJO and send it further along the route.
#Override
public void configure() {
from("Foo")
.bean(TestMessageTransformer.class, "testMessageTransform")
(1).marshal()
.to("Bar")
public class TestMessage {
private String content;
private String title;
public TestMessage(String content, String title) {
this.content = content;
this.title = title;
}
}
public class TestMessageTransformer {
#Bean("testMessageTransform")
public TestMessage transform(String message) {
return new TestMessage(message, "title");
}
}
(1) I've tried using marshal().jaxb(), marshal() with JacksonXMLDataFormat but no dice.
What exactly is the exception you get?
You may need to add public getters to TestMessage for the marshaller to get the fields.

Jaxb annotation #xmlelement(name="ElementName") not binding with xml element name

My Spring MVC Web Service code is as follows.
Model Class
#XmlRootElement(name="wrappedSecretData")
public class VendorData {
private long lKeyId;
#XmlElement(name="keyId")
public long getlKeyId() {
return lKeyId;
}
public void setlKeyId(long lKeyId) {
this.lKeyId = lKeyId;
}
}
Controller Method
#RequestMapping(value = "/vendor", method = RequestMethod.POST)
public String addVendor(#RequestBody VendorData vendorData) {
/*Checking recieved value*/
System.out.println(vendorData.getlKeyId());//**Returning 0 value **
return "Success";
}
Xml request body for web service
<wrappedVendorSecretsMetadata>
<keyId>1</keyId>
</wrappedVendorSecretsMetadata>
I am getting "0" value in lKeyId(Bold comment).
Where am I doing wrong.
Please provide the correct way to bind the xml element to object member using #XmlElement(name="keyId") annotation.
I think you need the #XmlElement only over the variable declaration.
try this:
#XmlRootElement(name="wrappedVendorSecretsMetadata")
#XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
public class VendorData {
private long lKeyId;
public VendorData(){
}
#XmlElement(name="keyId")
public long getlKeyId() {
return lKeyId;
}
public void setlKeyId(long lKeyId) {
this.lKeyId = lKeyId;
}
}
By default, annotations doesn't work with XmlMapper in jaxb. You have to register the annotation module for this purpose as I have done in the following code block:
String xmlData = getMyXmlData();
ObjectMapper objectMapper = new XmlMapper();
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyClass myObj= objectMapper.readValue(xmlData , MyClass.class);
In your case, you have to overwrite the Xml to Object binding process. To do that, you can receive the the HttpRequest in your controller and then convert the xml data to VendorData using your own java code.

JAXB unmarshal returning null values

I am using JAXB to convert XML file to java object
I have looked a lot in the examples on the web but still get null values in my object when I unmarshall it to a java object
what I miss?
File file = new File("BootloaderProtocol.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Command.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Command commanda = (Command) jaxbUnmarshaller.unmarshal(file);
System.out.println(commanda);
my class:
#XmlRootElement(name="Command")
public class Command {
String COMMAND_ID;
String COMMAND_NAME;
String COMMAND_CODES;
public String getCOMMAND_ID() {
return COMMAND_ID;
}
#XmlElement
public void setCOMMAND_ID(String COMMAND_ID) {
this.COMMAND_ID = COMMAND_ID;
}
public String getCOMMAND_NAME() {
return COMMAND_NAME;
}
#XmlElement
public void setCOMMAND_NAME(String COMMAND_NAME) {
this.COMMAND_NAME = COMMAND_NAME;
}
public String getCOMMAND_CODES() {
return COMMAND_CODES;
}
#XmlElement
public void setCOMMAND_CODES(String COMMAND_CODES) {
this.COMMAND_CODES = COMMAND_CODES;
}
}
and this my XML file:
<Command>
<COMMAND>
<COMMAND_ID>0xFE01</COMMAND_ID>
<COMMAND_NAME>Start bootloader</COMMAND_NAME>
<COMMAND_CODES>EE120301FE0900</COMMAND_CODES>
</COMMAND>
</Command>
This is the correct xml structure:
<Command>
<COMMAND_ID>0xFE01</COMMAND_ID>
<COMMAND_NAME>Start bootloader</COMMAND_NAME>
<COMMAND_CODES>EE120301FE0900</COMMAND_CODES>
</Command>
Try your code with this xml. Use the correct path of the xml file in the code.
Btw, I have tested your code with this xml and it works fine.
try something like this
public class Command {
#XmlElement(name="COMMAND")
public NestedCommand command;
static class NestedCommand {
String COMMAND_ID;

XMLRootElement converting a class to XML in jersey

I'm very good in converting a model class to JSON Array or Object.
But i'm a noob when it comes to XML.
I want my final output to be like this
<Response>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>
</Response>
To achieve it i create a model class
#XmlRootElement(name = "Response")
public class Response {
private Say say = new Say();
public Say getSay() {
return say;
}
public void setSay(Say say) {
this.say = say;
}
#XmlRootElement(name = "Say")
static class Say {
#XmlAttribute
private String voice = "alice";
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
}
Now after converting it to XML with jersey my output was
<Response>
<say voice="alice">
<string>Thanks for trying our documentation. Enjoy!</string>
</say>
</Response>
I got a extra string tag. I'm not sure what attribute to set for the String so that it comes in the body.? Or is there any other way?
Also for say. The 'S' is not capitalised. How can i make it a capital letter?
Thanks in advance
By default properties and public fields will be mapped to elements. What you want to do is use #XmlValue to map the field to the element's value.
#XmlRootElement(name = "Say")
#XmlAccessorType(XmlAccessType.FIELD)
static class Say {
#XmlAttribute
private String voice = "alice";
#XmlValue
private String string = "Thanks for trying our documentation. Enjoy!";
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
Note the use of #XmlAccessorType(XmlAccessType.FIELD). This is so the default behavior doesn't "doubly" attempt to map the property defined by the getter and setter. Alternatively, you could place the annotations on the getter, and leave out the the #XmlAccessorType
Result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Response>
<say voice="alice">Thanks for trying our documentation. Enjoy!</say>
</Response>
public class ResponseTest {
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Response.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Response response = new Response();
marshaller.marshal(response, System.out);
}
}
UPDATE
but can i know why the 'S' in Say is not capitalised even though #XmlRootElement(name = "Say") is specified?
You need to specify the name with #XmlElement(name = "Say") on the property. If you don't the default naming will kick in.
#XmlElement(name = "Say")
public Say getSay() {
return say;
}
The XmlRootElement(name = "Say") is only for if the element is used as the root element. For instance this:
Response.Say response = new Response.Say();
marshaller.marshal(response, System.out);
Would give you this output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Say voice="alice">Thanks for trying our documentation. Enjoy!</Say>

REST xml answer - Jaxb - Amazon product API

I'm currently (trying) to work with the amazon product API to search thourgh items.
I have the response in XML, but i have an exception in jaxb, maybe i missed something..
Here is the XML :
XML response from Amazon
I want to extract items informations, but i'm getting some trouble.
Item class:
#XmlRootElement(name="ItemSearchResponse")
public class AmazonItem
{
private String name;
private String asin;
private String price;
public AmazonItem()
{
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="Title")
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="ASIN")
public String getAsin()
{
return asin;
}
public void setAsin(String asin)
{
this.asin = asin;
}
#XmlJavaTypeAdapter(CollapsedStringAdapter.class)
#XmlElement(name="FormattedPrice")
public String getPrice()
{
return price;
}
public void setPrice(String price)
{
this.price = price;
}
}
my builder :
public class AmazonItemBuilder
{
public AmazonItemBuilder()
{
}
public List<AmazonItem> build(InputStream response)
{
try
{
JAXBContext context = JAXBContext.newInstance(AmazonItem.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
AmazonItem newItem = (AmazonItem) unMarshaller.unmarshal(response);
System.out.println(newItem);
}
catch (JAXBException e)
{
e.printStackTrace();
}
return null;
}
}
the "response" come from a URL response.openStream();
OK i forgot the error -_-
javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://webservices.amazon.com/AWSECommerceService/2011-08-01", local:"ItemSearchResponse"). Expected elements are <{}ItemSearchResponse>
Thank you !
It appears that the XML document is namespace qualified. You can use the package level #XmlSchema location annotation to specify the namespace qualification for the entire document. Package level annotations go on a special call called package-info that looks like the following:
package-info
#XmlSchema(
namespace = "http://webservices.amazon.com/AWSECommerceService/2011-08-01",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.your.pkg;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
I hope the top level class to covert the response to object should be "ItemSearchResponse", try creating a class with member variable "Items" which inturn will have another member object array "AmazonItem"

Categories

Resources