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

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

Related

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

How can I use same JAXB class to marshall and unmarshall into different namespaces?

Note: this is with Java 7. Also, I am not using JAXB to generate anything from a schema. I only/merely want to use JAXB to deserialize pieces of existing SOAP messages. So the JAXB beans are "hand-written".
I am trying to deserialize the following XML with JAXB. I do not have access to it as a string. Rather, I am getting it as an org.w3c.dom.Element object:
<SynchInfo xmlns="urn:InitronSOAPService">
<HandshakeInfo>
<dnsName>foo.bar</dnsName>
<ip>1.2.3.4</ip>
<id>TheId</id>
<key>SomeKey</key>
<version>1.0</version>
</HandshakeInfo>
</SynchInfo>
But I would also like to marshall HandshakeInfo instances into a SOAPBody that is part of a message belonging to a different namespace.
I first tried to write a HandshakeInfo class that didn't mention namespaces at all:
#XmlRootElement(name = "HandshakeInfo")
#XmlAccessorType(XmlAccessType.NONE)
final private static class HandshakeInfo
{
private String version;
private String id;
private String key;
private String ip;
private String dnsName;
public String getVersion() {
return version;
}
#XmlElement(name = "version")
public void setVersion(String version) {
this.version = version;
}
public String getId() {
return id;
}
#XmlElement(name = "id")
public void setId(String id) {
this.id = id;
}
public String getKey() {
return key;
}
#XmlElement(name = "key")
public void setKey(String key) {
this.key = key;
}
public String getIp() {
return ip;
}
#XmlElement(name = "ip")
public void setIp(String ip) {
this.ip = ip;
}
public String getDnsName() {
return dnsName;
}
#XmlElement(name = "dnsName")
public void setDnsName(String dnsName) {
this.dnsName = dnsName;
}
}
But when I used that to try to deserialize the Element object containing the HandshakeInfo element, I got the following exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"urn:InitronSOAPService", local:"HandshakeInfo"). Expected elements are <{}HandshakeInfo>
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
...
So then I tried adding , namespace="urn:InitronSOAPService" to all the element and attribute (in this case none) annotations in the class. (Or, alternatively, used an #XmlSchema annotation in a package-info.java file.) That worked for deserializing that element in that namespace.
But when I tried to use the class to serialize into a different namespace I (not entirely unexpectedly) get stuff like this:
<SomeMessage xmlns="urn:differentNamespace">
<ns5:HandshakeInfo xmlns:ns5="urn:InitronSOAPService">
<ns5:dnsName>foo.bar</ns5:dnsName>
<ns5:ip>1.2.3.4</ns5:ip>
<ns5:id>TheId</ns5:id>
<ns5:key>SomeKey</ns5:key>
<ns5:version>1.0</ns5:version>
</ns5:HandshakeInfo>
</SomeMessage>
That's not what I want. I want the serialized thing to be part of the urn:differentNamespace.
So is there any way to use the same JAXB class in multiple namespaces? Or am I stuck with having to multiple copies of classes with each one annotated to be in the appropriate namespace?
I have seen some tricks using filters, etc. but those tricks seem to be assuming the XML is being read from or written to a stream or a file. Again, for unmarshalling I am working with org.w3c.dom.Element objects and for marshalling I am working with SOAPBody objects.
I see the following options:
Use MOXy XML Bindings and move mappings out of annotations into XML resources. You could then define and load different resources for different namespaces.
Use XSLT to preprocess XML prior to unmarshalling/postprocess after marshalling. Remapping namespaces is a trivial task in XSLT.
Use a custom AnnotationReader for JAXB RI to "fake" namespace read from annotations. You just have to adjust the namespace read from the package, should not be too hard.
AnnotationReader solution is a bit "hacky", also ties you to JAXB RI. XML bindings would require you to rewrite your annotations in XML form and will tie you to MOXy (I think). XSLT is quite elegant but will make the processing slower. And you'll have to hack into the processing chain with your XSLT.
All the options have pros and cons, you have to carefully consider them for your specific case.

xstream unserialize complex XML to simpler java with Converter

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

Parse XML into DOM tree with custom object implementations in Java

I want to parse an XML document into a DOM tree in Java such that certain objects (e.g. instances of org.w3c.dom.Node or org.w3c.dom.Element) in the tree can be downcast to instances of classes that I have created, while minimizing the amount of XML-related code that I need to (re)implement. As a (very simple) example, if I have an XML element like:
<Vector size="5">
1.0 -1.0 3.0 -2.73e2
</Vector>
I would like to customize the parser to instantiate the following for it:
public class Vector extends /* some parser class */ {
private double[] elements;
/* constructors; etc.*/
public double dotProduct(Vector v) {
/* implementation */
}
}
such that I can pass instances of Vector created by the parser to, for example, javax.xml.xpath objects' methods and have them work correctly. What is the quickest way to achieve this? Is it possible with Java SE alone, or are third-party libraries (e.g. Xerces) necessary?
I'm not sure what your requirements are, but assuming you're in control of what the XML looks like, what I would use is XStream. It will allow you to skip all the DOM manipulation completely.
Now from their 2 minute tutorial, it may not seem like it's built for this use case, but it actually is. You create your java classes first, make sure they generate the XML the way you want it to look, and then use it to read your already existing XML back into your program as XStream objects. It's a very pleasant library to use.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
The Binder mechanism in JAXB may be what you are looking for. It doesn't allow a DOM node to be cast to a domain object, but it does maintain a link between a domain object and its corresponding DOM node.
Note: The following code ran clean when using the MOXy as the JAXB provider, but threw an exception when using the impl of JAXB included in the version of the JDK I happen to be running.
JAVA MODEL
I will use the following domain model for this example.
Customer
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Customer {
private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
#XmlElementWrapper
#XmlElement(name="phoneNumber")
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
}
PhoneNumber
import javax.xml.bind.annotation.*;
public class PhoneNumber {
private String type;
private String number;
#XmlAttribute
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#XmlValue
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
XML (input.xml)
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<phoneNumbers>
<phoneNumber type="work">555-1111</phoneNumber>
<phoneNumber type="home">555-2222</phoneNumber>
</phoneNumbers>
</customer>
DEMO CODE
In the demo code below I will do the following:
Use XPath to find a child element, then use the Binder to find the corresponding domain object.
Update the domain object and use the Binder to apply the change to the DOM.
Update the DOM and use the Binder to apply the change to the domain object.
Demo
import javax.xml.bind.Binder;
import javax.xml.bind.JAXBContext;
import javax.xml.parsers.*;
import javax.xml.xpath.*;
import org.w3c.dom.*;
public class Demo {
public static void main(String[] args) throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse("src/forum16599580/input.xml");
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Binder<Node> binder = jc.createBinder();
binder.unmarshal(document);
// Use Node to Get Object
Node phoneNumberElement = (Node) xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.NODE);
PhoneNumber phoneNumber = (PhoneNumber) binder.getJAXBNode(phoneNumberElement);
// Modify Object to Update DOM
phoneNumber.setNumber("555-2OBJ");
binder.updateXML(phoneNumber);
System.out.println(xpath.evaluate("/customer/phoneNumbers/phoneNumber[2]", document, XPathConstants.STRING));
// Modify DOM to Update Object
phoneNumberElement.setTextContent("555-2DOM");
binder.updateJAXB(phoneNumberElement);
System.out.println(phoneNumber.getNumber());
}
}
Output
555-2OBJ
555-2DOM
I have been through exactly this over the last 10 years, building XML DOMs for chemistry, graphics, maths, etc. My own solution has been to use a DOM where the elements can be subclassed (I use xom.nu but there are others). The w3c dom does not allow subclassing (IIRC) and so you would have to build a delegate model. (I tried this many years ago and rejected it, but software tools and libraries make all this much easier (e.g. the IDE will generate delegat methods).
If you are doing a lot, and especially if you are creating a lot of custom methods then I would recommend rolling your own system. The effort will be in your methods (dotProduct), not the XML.
Here, for example, is my class for a 3D point.
public class CMLPoint3 extends AbstractPoint3
(which extends the base class CMLElement, which extends nu.xom.Element
The creation of elements is a factory. Here's a chunk of my SVGDOM:
public static SVGElement readAndCreateSVG(Element element) {
SVGElement newElement = null;
String tag = element.getLocalName();
if (tag == null || tag.equals(S_EMPTY)) {
throw new RuntimeException("no tag");
} else if (tag.equals(SVGCircle.TAG)) {
newElement = new SVGCircle();
} else if (tag.equals(SVGClipPath.TAG)) {
newElement = new SVGClipPath();
} else if (tag.equals(SVGDefs.TAG)) {
newElement = new SVGDefs();
} else if (tag.equals(SVGDesc.TAG)) {
newElement = new SVGDesc();
} else if (tag.equals(SVGEllipse.TAG)) {
newElement = new SVGEllipse();
} else if (tag.equals(SVGG.TAG)) {
...
} else {
newElement = new SVGG();
newElement.setClassName(tag);
System.err.println("unsupported svg element: "+tag);
}
if (newElement != null) {
newElement.copyAttributesFrom(element);
createSubclassedChildren(element, newElement);
}
return newElement;
You can see tools for copying and recursing.
The questions you need to think about are:
how closely is this bound to an XSD
do I use XSD data Types
do I validate on input
am I using the DOM as the primary data structure (I do)
how frequently will things change.
FWIW I have been through 6 revisions of this and am contemplating another (using Scala as the main engine).

JAX-RS unrmashalling with #XmlIDREF

I have been looking into this issue for hours now, probably simple but I don't get it anymore:
I have an entity (Param) which is rendered to json via jax-rs. The entity references another entity (Step).
When writing / reading json, I dont want to see the whole step-entity but merely its id, so I use this code :
#Entity
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Param implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
long id;
#Column(name = "KEYNAME")
String key;
String value;
#XmlIDREF
Step step;
}
Works perfectly for marshalling.
So any GET-request shows me something the following:
{id: 1,
key: "a",
value: "b",
step: 53
}
But when I post some param to the server, it cant map back the numeric id to a step-entity.
I need to provide the unmarshaller with a custom IDResolver. But how can I configure the unmarshaller???? The Jax-RS servlet is doing the marshalling for me. My code looks like that:
#Path("param")
public class ParamRepresentation {
/**
* Retrieves representation of an instance of ParamRepresentation
* #return an instance of Param
*/
#GET
#Produces("application/json")
#Path("{ID}")
public Param getJson(#PathParam("ID") long id) {
return (Param) ctr.find(id, Param.class);
}
#PUT
#Path("{ID}")
#Consumes("application/json")
#Produces("application/json")
public SuccessMessage updateStep(#PathParam("ID") long id, Param p) {
ctr.update(p);
ParamSuccessMessage sm = new ParamSuccessMessage();
sm.setSuccess(true);
sm.setParam(p);
return sm;
}
}
so how can i configure the unmarshaller ?????
I think you've misunderstood the purpose of IDREF in XML schemas. It's there to allow you to refer to another element that is marked as an ID (i.e., with an #XmlID annotation in JAXB) in the same document. You can't use it to refer to an ID elsewhere in the world; for that you'd use a URI (possibly with a fragment identifier part). To do those in JAXB, you use:
#XmlElement // You might be able to omit this part; it's here for clarity
#XmlSchemaType(name = "anyURI")
public URI exampleField;
You then need to work out whether the URI refers to something you know (i.e., resolve the URI and see if it points into yourself) and deal with the fragment identifier. Or do the more common trick of just using a string and don't worry about trying to magically hook everything up in the binding layer. (That works rather well in practice.)
I've done a similar thing using Jersey and xml representations. I used an xml adapter to symmetrically map between the complete child element and the partial (just id) element.
I would annotate the Step entity in your Param entity as follows:
//javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter
#XmlJavaTypeAdapter(PartialStepEntityAdapter.class)
Step step
You would then need to define both the partial Step entity and the Adapter. The PartialStep would be identical to your original step class, but with just the id field.
The PartialStepEntityAdapter would map a Step to a PartialStep when marshalling and a PartialStep to a Step when unmarshalling:
//javax.xml.bind.annotation.adapters.XmlAdapter
public class PartialStepEntityAdapter extends XmlAdapter<PartialStep, Step> {
#Override
public Step unmarshal(PartialStep partialStep) throws Exception {
Step step = new Step();
step.setId(partialStep.getId());
return step;
}
#Override
public PartialStep marshal(Step step) throws Exception {
PartialStep partialStep= new PartialStep();
partialStep.setId(step.getId());
return partialStep;
}
}
Hope that's some help.

Categories

Resources