Using XStream 1.2.2
The XML document:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" protocol="OCI" xmlns="C">
<sessionId xmlns="">192.168.1.19,299365097130,1517884537</sessionId>
<command xsi:type="AuthenticationRequest" xmlns="">
<userId>me#somewhere.com</userId>
</command>
</Document>
I'm trying to parse into to a Document;
public class Document {
private String sessionId;
public Command command;
public Command getCommand() {
return this.command;
}
public void setCommand(Command command) {
this.command = command;
}
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
}
Parsing code is:
XStream xstream = new XStream();
xstream.alias("Document", Document.class);
xstream.alias("sessionId", String.class);
xstream.alias("command", Command.class);
xstream.alias("userId", String.class);
Document doc = (Document) xstream.fromXML(theInput, Document.class);
but this throws:
java.lang.ClassCastException: java.lang.Class cannot be cast to com.mycompany.ocip.server.model.Document
because the returned object from fromXml is of type: Class<com.mycompany.ocip.server.model.Document>
Shouldn't I expect it to return a com.mycompany.ocip.server.model.Document instance?
That needs to be:
Document doc = (Document) xstream.fromXML(theInput);
If you pass in a second parameter, XStream will try to populate that with the values from the XML. Since in your code, you're passing in a class object, XStream will try to populate the class object and return it.
The JavaDoc has the details.
Related
Using either Jackson or JAXB, is there a way to deserialize/marshal an entire subtree of child XML elements into a String?
For example, given the following XML:
<root>
<foo>
<bar>
<baz/>
</bar>
</foo>
</root>
Is there a way to deserialize/marshal that to the following class
#XmlRootElement
public class Root {
private String foo;
// constructors, getters, setters, etc.
}
where the result would look like Root(foo="<bar><baz/></bar>")?
You can write your own jackson deserializer for this:
public class Test {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new XmlMapper();
final SimpleModule module = new SimpleModule("configModule", Version.unknownVersion());
module.addDeserializer(Root.class, new DeSerializer());
mapper.registerModule(module);
// Root readValue = mapper.readValue(<xml source>);
}
}
class DeSerializer extends StdDeserializer<Root> {
protected DeSerializer() {
super(Root.class);
}
#Override
public Root deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
// use p.getText() and p.nextToken to navigate through the xml and construct Root object
return new Root();
}
}
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.
I'm converting a JSON file into an ArrayList and then to XML by using Jackson. It is displayed in Firefox but just as a normal String. By using the inspect element tool I get the whole formatted xml though. Which function can I use to display it correctly on the browser?
My method:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
}
Desired output in browser:
<myclass xmlns="">
<myclass>XyClass</ci>
<myname>XyName</ci>
...
</myclass>
Actual output:
XyClassXyName...
Quite simplified the class looks like this:
#JacksonXmlRootElement(localName ="MyClass")
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class MyClass {
#XmlElement(required = true)
private String class;
#XmlElement(required = true)
private String name;
//....
//standard constructor
public MyClass() { }
public CI(String class, String name){
this.class = class;
this.name = name;
}
public String getClass() {
return class;
}
public String getName() {
return name;
}
public void setClass(String class) {
this.class = class;
}
public void setName(String name) {
this.name = name;
}
}
Another weird thing is that I have the exact annotations in another class, trying the same thing with that and there the browser does not display anything... Thanks for any help.
Well my mistake derived from two things basically. Most important is the definiton of the XML Root Element (not only as annotation in your "MyClass"). Define a global String to create a XML Root element, otherwise your document won't be well-formed and the mistake "junk after document element" will be shown.
private String asXml ="<?xml version=\"1.0\" encoding=\"utf-8\"?><MyList>";
I've adapted the method to:
private void init() throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
InputStream is = MyClass[].class.getResourceAsStream("/config/myList.json");
myList= Arrays.asList(mapper.readValue(is, MyClass[].class));
XmlMapper xmlMapper = new XmlMapper();
for(MyClass test : myList){
String asXml += xmlMapper.writer().with(SerializationFeature.WRAP_ROOT_VALUE).withRootName("MyClass").writeValueAsString(test);
}
LOGGER.info("asXml: {}.", asXml);
asXml += "</MyList>";
}
And don't forget to add the correct MediaType in your RestController:
#RequestMapping(value="/display", method=RequestMethod.GET, produces=MediaType.APPLICATION_XML_VALUE)
public #ResponseBody String getList(Model model) {
return service.getAsXmlString();
}
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;
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>