deserialize xml polymorphic list with jackson - java

I have problem deserializing following xml:
<root>
<apples>
<apple>
<id>1</id>
<weight>0.6</id>
</apple>
<apple>
<id>2</id>
<weight>0.7</id>
</apple>
</apples>
</root>
to java:
public class Root {
private List<Fruits> fruits;
}
In above xml source other variants may be eg oranges/orange etc. also since this is propertary xml I cannot change it's schema. By default I am using #JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT) and #JsonSubTypes({/*fruits subtypes listed*/}). After few unsuccessful attempts I am not sure what is the proper mapping/configuration to solve the problem.

Out of the box, Jackson is a JSON parser, not XML.
There are extensions for Jackson that allow for XML conversion. See http://wiki.fasterxml.com/JacksonExtensionXmlDataBinding for more info there. But it seems as though a pure XML parser may be better for this case.
Perhaps the javax.xml.stream.* packages.
i.e. http://www.javacodegeeks.com/2013/05/parsing-xml-using-dom-sax-and-stax-parser-in-java.html for more info.

Related

Jackson XmlMapper: Custom Deserializer XML child node structure as string

I would like to deserialize an XML subtree as string (with Jackson) in JAVA:
input structure:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<nodeA>text</nodeA>
<nodeB>
<nodeS>
<nodeS1>...</nodeS1>
<nodeS2>...</nodeS2>
</nodeS>
</nodeB>
</root>
into something like this:
public class Pojo {
#JacksonXmlProperty(localName="nodeA")
private String nodeA; // = "text"
#JacksonXmlProperty(localName="nodeB")
#JsonDeserialize(using = MyXmlDeserializer.class)
private String nodeB; // = "<nodeS><nodeS1>...</nodeS1><nodeS2>...</nodeS2></nodeS>"
}
The node nodeS should be taken as "raw-value" without ANY modifications to the xml and put it into a String class member.
I've tried it with custom deserializer or as #JacksonRawValue with no avail.
if one just could access the raw value of the "currentNode", that would help a lot.
Any alternative (jackson-related) solutions welcome :-)
It's kind of a workaround rather than a solution, but it's possible to use XmlMapper inside your JsonDeserializer:
xmlMapper.writeValueAsString(jsonNode);
As a result you'll get Xml string again. But as I wrote, it's workaround and I don't like it gets from Xml to Json, and from Json to Xml again.
If anyone get a better solution (using Jackson), please share.

JAVA XML Parsing with namespaces using JAXB annotations

I have a huge XML response from an external end point. I want to parse the XML response to java classes. I was able to parse into respective POJOS if none of the XML had namespaces and things went well till that point.
However, the response might contain namespaces only at the root element.
For eg, like this
<?xml version="1.0" encoding="UTF-8"?>
<Document xmlns="somevalue here"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RestOfTheDocument> something here </RestOfTheDocument>
</Document>
I can guarantee that none of the inner xml elements will have any more namespaces.
Is there a way to implement this? I saw a few answers ignoring namespaces completely but dont feel very convincing. Is there a way to properly parse these.
This is my java class to model the XML response
#XmlRootElement(name = "Document")
static class Response {
#XmlElement(name = "RestOfTheDocument", required = true, nillable = false)
RestOfTheDocument restOfTheDocument;
}
what I tried ?
Add namespace information to the #XmlRootElement like this
#XmlRootElement(name = "Document", namespace = "somevalue here")
Doing so is making all the inner xml elements to be NULL.
NOTE: I have abstracted the huge inner level to be RestOfTheDocument but there are lot many but none of them will have any namespaces whatsoever in the response!
Thank you!
SimpleXml will ignore the namespaces and just parse the XML:
public class Document {
public String RestOfTheDocument;
}
final String data = ...;
final SimpleXml simple = new SimpleXml();
final Document document = simple.fromXml(data, Document.class);
System.out.println(document.RestOfTheDocument);
This will output:
something here
From maven central:
<dependency>
<groupId>com.github.codemonstur</groupId>
<artifactId>simplexml</artifactId>
<version>1.4.0</version>
</dependency>

How to remove namespace definition from jackson Xml parsing

I'm using Jackson data format for serializing Pojos as XML.
It is working fine but I would like to remove the namespace definition:
#JacksonXmlRootElement(localName="simple_something")
public class Simple {
public int x = 1;
public int y = 2;
}
I do:
ObjectMapper xmlMapper = new XmlMapper();
String xml = xmlMapper.writeValueAsString(new Simple());
I get:
<simple_something xmlns="">
<x>1</x>
<y>2</y>
</simple_something>
but I would like to remove the xmlns=""
and that it looks like
<simple_something>
<x>1</x>
<y>2</y>
</simple_something>
Any ideas?
Make sure to use Woodstox Stax implementation, and not Stax implementation Oracle bundles with JDK. This is usually done by adding Maven dependency to explicitly include woodstox jar.
This is explained on XML module README at https://github.com/FasterXML/jackson-dataformat-xml/
Oracle's implemention adds that declaration in namespace-repairing mode for some reason. It is also slower and has more bugs, so there's not much reason to rely on it, unless you really want to minimize external dependencies.
Also note that that namespace declaration is completely benign, so while unnecessary it is legal XML. So while it is eyesore all xml tools should work just fine with such extra declarations.

How to ignore/disable/revert/override JAXB class-level #XmlJavaTypeAdapter in certain situations?

Our model classes are annotated with #XmlJavaTypeAdapter (at the class-level). Unmarshalling works fine for the root element and containment/nesting (according to what we implemented in our custom XmlAdapter).
So far, we were happy campers for both XML and JSON serialization/deserialization. However, a new need arose and I can't figure out how to implement it ?
In certain situations, I'd like to be able to "revert" to default JAXB behavior for containment: I want the class-level #XmlJavaTypeAdapter annotation to be ignored/overriden.
I spent hours reading Blaise Doughan's blog (http://blog.bdoughan.com/) and searching StackOverflow and Google but can't find an elegant/pragmatic solution.
Here is a quick setup to illustrate what we currently have (please note that all our JPA/Hibernate/other annotations are not listed for simplicity-sake but they do exist in our model classes (POJOs)):
Class Master
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlMasterAdapter.class)
public class Master {
#XmlElement
private Long masterPrimaryKey;
#XmlElement
private String name;
}
Class Detail
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlDetailAdapter.class)
public class Detail {
#XmlElement
private Long detailPrimaryKey;
#XmlElement
private Master master; // reference/foreign key. No need for #XmlJavaTypeAdapter since it's defined at the class-level in Master.
#XmlElement
private String value;
}
When Master is used as a the root element, the XML is like this:
<master>
<masterPrimaryKey>1234</masterPrimaryKey>
<name>master name</name>
</master>
When Master is used as a contained/nested element, the XML is like this: (thanks to our custom XmlAdapter, the <master> element is "summarized" by its primary key)
<detail>
<detailPrimaryKey>5678</detailPrimaryKey>
<master>1234</master>
<value>detail value</value>
</detail>
So far, everything works fine and we're happy with it.
Now, our new need:
I'd like containment to work in a different way in specific situations.
I want the class-level #XmlJavaTypeAdapter on Master to "temporarily" be ignored/reverted/overridden in a specific context. I'd expect the default JAXB unmarshaller to kick-in (as if there had never been a class-level #XmlJavaTypeAdapter on the contained classes).
Think about a data-import situation where we receive the master and all the details in one payload. As if they were all independent root elements wrapped in a big DTO/transport container.
Here is the XML presenting what we want:
<masterDetailImport>
<master>
<!-- Primary keys omitted because of the import mode -->
<name>master name</name>
</master>
<details>
<detail>
<value>detail 1 value</value>
</detail>
<detail>
<value>detail 2 value</value>
</detail>
<detail>
<value>detail 3 value</value>
</detail>
</details>
</masterDetailImport>
Class MasterDetailImport
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class MasterDetailImport implements Serializable
{
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private Master master;
#XmlElementWrapper(name="details")
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private List<Detail> detail = new ArrayList<Detail>();
}
What I'm looking for is the magic [yet non-existing] #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT annotation that would allow me to instruct JAXB to do as if #XmlJavaTypeAdapter had never been defined at the class-level for the nested classes.
So far, the solutions we envisioned [and don't like] are:
Create "mirror" DTO objects for deserialization only when we must support import. The are many cons with this approach (duplicate code only used for deserialization, adapters to copy the DTO content into the model class, more unit tests to write/maintain, etc).
Get rid of class-level #XmlJavaTypeAdapter on all our entities we want to be able to import/nest and explicitly use #XmlJavaTypeAdapter on all attributes where nesting/containment is used. I tested this approach and know it would work. However, I think it's error prone and not as elegant as defining it at class-level and be able to have an exception/special-case/override handling telling JAXB to temporarily behave as if it never knew #XmlJavaTypeAdapter has been defined on the class.
I'm running out of ideas here... I tried looking for JAXB's default XML adapter but was not successful: javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> is abstract and inherits from Java.lang.Object.
Now, the simple question:
How to implement #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT ?
Thanks in advance !
An XmlAdapter in JAXB will always be applied, but you can put logic in the XmlAdapter itself to handle your use case. By default a new instance of XmlAdapter will be created each time it will be used, if your XmlAdapter is stateful you can set an instance on the Marshaller or Unmarshaller so that it will be used instead. You can leverage this to help determine if it should be applied or not.
Below is a link to an answer I gave to a related question where a stateful XmlAdapter is used to inline an object the first time it is reference, and then marshal it as a link each subsequent time it is referenced.
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?

Handling different object types contained within the same set of elements

I have an XML document that looks something like the following:
Note that I cannot change the schema because it is part of a standard XML Schema (Library of Congress METS).
<amdSec ID="AMDSEC001">
<digiprovMD ID="DMD001">
<mdWrap MDTYPE="OBJECT">
<xmlData>
<object xsi:type="file">
.....
</object>
</xmlData>
</mdWrap>
</digiprovMD>
<digiprovMD ID="DMD001_EVENT">
<mdWrap MDTYPE="EVENT">
<xmlData>
<event xsi:type="event">
.....
</event>
</xmlData>
</mdWrap>
</digiprovMD>
</amdSec>
As you can see, the inner element <mdWrap> can contain elements of different types; in this case they're <event> and <object>, but it isn't constrained to just those two types. Creating two classes (like below), marshals okay, but this doesn't work for unmarshalling.
class ObjectMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "object")
List<MyObject> object; //Wrapped in list to use #XmlElementWrapper
}
class EventMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "event")
List<MyEvent> event; //Wrapped in list to use #XmlElementWrapper
}
What can I do so that JAXB unmarshals the correct "type" of MDWrap?
I think, the best solution in this case is a generating POJO classes using XJC tool.
Download XSD file which describe XML file.
Using XJC tool convert XSD file into POJO classes. If XSD is not correct - fix it.
Make some changes if you need in generated classes.
Use this classes in marshalling / unmarshalling process.
I was able to figure out the solution, and it's much simpler than I initially thought (which speaks to my relative inexperience with XML and JAXB). By creating my MDWrap class in the following way
class MDWrap {
#XmlAnyElement(lax = true)
#XmlElementWrapper(name = "xmlData")
Object wrappedMD;
}
Then MDWrap can contain an object of any type, and will unmarshal properly, as long as the class of which wrappedMD is an instance of is annotated with #XmlRootElement. The trick is to annotate wrappedMD as XmlAnyElement.

Categories