xstream unserialize complex XML to simpler java with Converter - java

I have surely a problem of understanding of Xstream converter.
I have a pretty complex XML coming from legacy back office application and a just want to convert this one into a simpler java class or a Map with Converter.
I do not want to marshal to XML, just unmarshal
I do not want to use annotation, just KISS
My XML is like
<root>
<agent>
<id>123456789</id>
<name>GABIN</name>
<forname>Jean</forname>
<unit>
<legacyReference>AA</legacyReference>
<name>SAN ANTONIO</name>
<legacyName>SA</legacyName>
<address>
<number>1</number>
<street>Sesam street</street>
<city>NoWhere</city>
<legacyID>AAZE-56</legacyID>
</address>
<legacyStructurBlablabla>
<type>OFFICE</type>
<data>
<foo>BAR</foo>
</data>
</legacyStructurBlablabla>
<...>
</unit>
<...>
</agent>
</root>
My destination class is very simple
class Agent {
String id;
String name;
String forname;
String unitName;
String unitType;
<...>
}
My main method looks like
xStream = new XStream(new DomDriver());
stream = this.getClass().getClassLoader().getResourceAsStream("data/agent.xml") ;
xStream.alias("root", Map.class);
xStream.alias("agent", Agent.class);
xStream.registerConverter(new ResultConverter());
xStream.registerConverter(new AgentConverter());
xStream.ignoreUnknownElements();
Object obj = xStream.fromXML(stream);
I don't understand how to step down in favor of another converter.
public class ResultConverter implements Converter {
...
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
Map<String, Object> agents = new HashMap<String, Object>();
while( reader.hasMoreChildren() ) {
reader.moveDown();
// HOW I DO TO STEP DOWN IN FAVOR OF Agent CONVERTER ?
reader.moveUp();
}
return agents;
}
I didn't see Xstream scrolled dow the thierarchy to activate my AgentConverter.
I surely missing the point
EDIT
Xstream may be not thr right tool for this.
I would use xmlbeam, according to Cfx advise.
XMLBeam projects the XML to a Java Class according to XPATH mapping.

Seems that you want the xml structure not to be reflected in your Java class. If you stick to XStream, I have no solution. But there is an alternative framework that was made to solve exactly this issue. It uses annotations, but IMHO does not violate the KISS principle.
Here is an example:
public interface Agent {
#XBRead("/root/agent/id")
String getId();
#XBRead("/root/agent/name")
String getName();
#XBRead("/root/agent/forename")
String getForname();
#XBRead("/root/agent/unit/name")
String getUnitName();
#XBRead("/root/agent/unit/legacyStructurBlablabla/type")
String getUnitType();
}
You will define interfaces instead of classes, but the instances of these interfaces are useable just like POJOs. (with toString(), equals(), hashCode,...).
Creating instances of these interfaces is like this:
Agent agent = new XBProjector().io().stream(stream).read(Agent.class);

Related

How to add wrapper element to class during the JAXB marshalling

I am trying to create XML using the Moxy Marshalling approach. Everything seems to be working fine except for one small thing. Basically, I would like to know how to add wrapper element to the class itself during the marshalling.
As we are aware we can add #XmlPath("test/name/text()") to add a wrapper to any of the String elements. Also for collection we can make use of #XmlElementWrapper(name="languages"), #XmlElement(name="language"). But these are for the fields within the class. How can I add the wrapper element to the class itself which is being marshalled?
I have the following class:
#XmlRootElement(name = "customer")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer{
#XmlPath("extension/name/text()")
private String name;
#XmlElementWrapper(name = "languages")
#XmlElement(name = "language")
private List<String> languages;
//Getter, Setter and other constuctors
}
This will produce the output XML something like this:
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
However, I would like to add the wrapper element to event the whole class so that output would look something like this: (Notice the whole customer is wrapper within classWrapper)
<classWrapper>
<customer>
<extension>
<name>Hello</name>
</extension>
<languages>
<language>English</language>
<language>UK English</language>
</languages>
</customer>
</classWrapper>
I tried adding the #XmlPath and #XmlElementWrapper to the Customer class but it throws an error as it can be added only to the fields within the class but not to the whole class.
Following is my Main class which would be used for marshalling:
public class HelloWorld{
public static void main(String []args){
Customer customer = new Customer();
List<String> languages = new ArrayList<String>();
languages.add("English");
languages.add("UK English");
customer.setName("Hello");
customer.setLanguages(languages);
JAXBContext context = JAXBContext.newInstance(Customer.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(customer,System.out);
}
}
I am aware that I can write another wrapper class add the custom field to it then use that wrapper class for the marshalling. Actually, I am doing the same as of now. However, trying to find if there is some Moxy approach of doing this similar to how we have #XmlPath and #XmlElementWrapper
Can someone please suggest some form of Moxy Jaxb related approach for achieving the wrapper element to the whole class?
This is a temporary workaround that works as expected for me. Posting the answer here so it can be helpful to someone in the future.
Change the #XmlRootElement("customer") to #XmlRootElement("classWrapper"). So you will get the classWrapper as the outer element.
Then change all the element within the Customer class using the #XmlPath so that all element go under the Customer tag. So overall customer.class would look something like this:
#XmlRootElement(name = "classWrapper")
#XmlType(name="customer",propOrder={"name","languages"})
#XmlAccessorType(XmlAccessType.FIELD)
#Getter
#Setter
public class Customer{
#XmlPath("customer/extension/name/text()")
private String name;
#XmlPath("customer/languages/language/text()")
private List<String> languages;
//Getter, Setter and other constuctors
}
Just a tip while using the #XmlPath:
do not use text() if its not simple type such as String,Date, etc type.
For example if the element is of Custom type with List then do not use /text()
#XmlPath("extension/elements/element")
List<myType> elements;
This will add extension -> elements -> element then content.
<extension>
<elements>
<element></element>
<element></element>
......
</elements>
</extension>
If the elements are of String type then you have to use text()
#XmlPath("extension/elements/text()")
List<String> elements;

JAXB unmarshaller with custom interceptor?

Is it possible to "intercept" the unmarshalling process of JAXB?
I have an xml reponse that partially should be converted to a different java fields structure:
<xml>
<X_FIELD1></X_FIELD1>
<X_FIELD2></X_FIELD2>
...
<X_FIELD11></X_FIELD11>
</xml>
In my java class, I'd prefer to unmarshal this to a List<String>, instead of 11 String fields.
public class XmlResponse {
private String X_FIELD1;
private String X_FIELD2;
//...
private String X_FIELD11;
// private List<String> xFields;
}
But is that possible?
You should use a custom xml adapter
http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/XmlJavaTypeAdapter.html
http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/XmlAdapter.html
apply it on the class level and implement the adapter.

How to write pojo class as a data model for complex xml structure

I'm parsing an XML document which has a fairly complex data structure.
Example:
<Companies>
<LISTID>6353HHJDLS628JNHJ6</LISTID>
<Company>
<ID>123ABC</ID>
<Value>True</Value>
<Order>
<Text>Because </Text>
<ListOfReasons>
<InputName>
<Text>your company did not meet requirements</Text>
</InputName>
<Text>, </Text>
<InputName>
<Text>your company was not listed as qualified</Text>
</InputName>
<Text> etc...</Text>
</ListOfReasons>
</Order>
</Company>
<Company>
<ID>123DEF</ID>
<Value>False</Value>
<Order>
<Text>We can't get any more details on </Text>
<NodeName>
<Text>neither your company or the entity in question</Text>
</NodeName>
<Text> right now.</Text>
</Order>
</Company>
</Companies>
</root>
How should I model my pojo class? To me it seems it should have nested or inner classes. I am not sure how this would look like
I know all about JaxB but I don't really know how to use it and unless there is some easy way to implement it, I prefer writing a pojo, because I understand it.
I'm DOM parsing and I'd like to represent it in Java objects. That is the purpose for writing this model. Can anyone give me an example data model class using the XML I've shown. Any help or assistance would be much appreciated.
There's a great tool that will get you started here.
In your case there are classes like this:
#XmlRootElement
class Companies {
private String LISTID;
private List<Company> companies;
#XmlElement(name = "company")
public void setCompany(List<Company> companies) {
this.companies = companies;
}
#XmlElement
public void setLISTID(String LISTID) {
this.LISTID = LISTID;
}
/** Others standard POJO Methods */
}
#XmlRootElement
public class Company {
private String id;
private String value;
private List<Order> orders;
/** Like in the previous example**
}
With the tool showed above, your work will only to decorate classes with #Xml* annotations.

How do I programmatically create XML from Java?

I am trying to programmatically create XML elements using JAXB in Java. Is this possible? I am reading this page here for something I can use, but have so far found nothing.
Usually you start by defining a bean
#XmlRootElement public class MyXML {
private String name;
public String getName() { return name; }
#XmlElement public void setName(String s) { this.name = s; }
}
and serialize it with code like
public class Serializer {
static public void main(String[] args) {
MyXML m = new MyXML();
m.setName("Yo");
JAXBContext jaxbContext = JAXBContext.newInstance(MyXML.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(m, new File("MyXML_"+ ".xml"));
}
}
that whould produce the following XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myXML>
<name>Yo</name>
</myXML>
How would I program my Java class to create the element tag name depending on what is entered in the program? For instance in my example the tag element is called 'name'. How could I set this at runtime though? Is this possible with generics or some other way?
The B in JAXB stands for Bean so no, there's no way to use JAXB without defining beans.
You just want to dinamically create an XML so take a look at jOOX for example (link to full Gist)
Document document = JOOX.builder().newDocument();
Element root = document.createElement("contacts");
document.appendChild(root);
for (String name : new String[]{"John", "Jessica", "Peter"}) {
$(root).append(
$("contact"
, $("name", name)
, $("active", "true")
)
);
}
Here, you use annotation before compile-time while you have no knowledge yet of the format you will need.. Marshalling this way is not that different from serializing, and it basically map directly the fields of a java object to an XML representation --> (if something is not defined in the object, it won't appear in the representation).
What you thrive to do looks like simple xml crafting (a XML parser would be enough S(t)AX/DOM whatever -- I like Jackson).
For the sake of curiosity, if you really want to fiddle with annotation you can use a bit of reflection in conjonction with the answer you will find here

Using Java-to-schema mapped object as Freemarker data model

Background:
I have a webservice that previously only received requests for xml, but now needs to return html for browser access.
I have a Java class that is mapped to XML with the XmlRootElement annotation.
I am using Freemarker to generate HTML based on this Java class, but cannot find a way to do so directly.
At the moment I use NodeModel.parse to parse the xml to a freemarker datamodel, but since the NodeModel.parse takes a File, I first write the Java object to a file. That is obviously an inefficient way to do it, but it does the job.
Does anyone know a way to go get a freemarker datamodel out of a this Java class without first writing it to an XML file?
The following is my code:
The Java-to-Schema mapped class:
#XmlRootElement(name = "report")
public class Report {
private String id;
private String time;
public Report() {}
public String getTime() {return time;}
public void setTime(String time) {this.time = time;}
public String getId() {return this.id;}
public void setId(String id) {this.id = id;}
}
Merging the data with the template:
public String getReportsAsHtml(#QueryParam("lastUpdate") String lastUpdate){
MySQLAccess dao = new MySQLAccess();
List<Report> reports = dao.readReports(lastUpdate);
Template temp = TemplateConfiguration.getInstance().getTemplateConfiguration().getTemplate("list_template.ftl");
**HashMap<String, NodeModel> root = new HashMap<String, NodeModel>();**
**root.put("doc", NodeModel.parse(Java2XML.getXMLFromJava(reports)));**
StringWriter output = new StringWriter();
temp.process(root, output);
output.flush();
return output.toString();
}
NodeModel has a wrap(org.w3c.dom.Node) method, so you surely don't have to create an XML file. All you need is a tree of org.w3c.dom.Node objects, and FreeMarker doesn't care where it comes from. Actually, if you are using the default object-wrapper of FreeMarker, you don't even need to deal with NodeModel, just drop the org.w3c.dom.Node into the data model as any other POJO, and FreeMarker will recognize it as XML.
Also note that FreeMarker has this ObjectWrapper abstraction. It separates the actual objects from how they are seen from the templates. So you possibly doesn't even need to make a tree of Node-s from those objects, just make an ObjectWrapper implementation that directly understands those annotated object. See how DefaultObjectWrapper extends BeansWrapper, automatically wrapping Node-s, Jython object, etc. You can follow the same pattern. But of course writing your own ObjectWrapper is extra work, especially if you need XPath support too (hint: Jaxen doesn't need Node-s).
I have used the following code to generate a Node tree from a Java-to-Schema annotated class:
public static Node getNodeFromReport(Object report){
JAXBContext context = JAXBContext.newInstance(report.getClass());
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
DocumentBuilderFactory docFac = DocumentBuilderFactory.newInstance();
Document result = docFac.newDocumentBuilder().newDocument();
marshaller.marshal(report, result);
return result;
}

Categories

Resources