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
Related
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);
I'm trying to unmarshal the following XML using Jackson 2.4.0 XmlMapper:
<root>
<a/>
<b/>
</root>
...and the following POJO
class Root {
#JacksonXmlElementWrapper(useWrapping = false)
#JsonSubTypes({
#JsonSubTypes.Type(name = "a", value = POJO_A.class),
#JsonSubTypes.Type(name = "b", value = POJO_B.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I've also tried with JAXB #XmlElements annotations with the same result, which is:
Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"])
So it seems that Jackson thinks my list is called "objects" instead of "a" and "b". Normally I fix that by using #JsonProperty("newName") but in this case I expected that to be handled by the #JsonSubtypes or #XmlElements annotations.
As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?
Update: Forgot to say, the problem is deserializing to the same collection (because I need to keep the order and they can be mixed). Doing it in separate fields works just fine.
When I was trying peeskillet answer and changed Jackson for JAXB unmarshaller, using #XmlElements/#XmlElementRefs worked (#XmlAnyElement did not for some reason, I was getting a list of ElementNSImpl instead of my own classes).
It will be nice to have this working with Jackson, but in the meantime, this is the way to make this work.
UPDATE:
What I did was:
class Root {
#XmlElements({
#XmlElement(name = "a", type = PojoA.class),
#XmlElement(name = "b", type = PojoB.class)
})
public final List<AbstractPOJO> objects = new ArrayList<>();
}
I also added a #XmlRootElement(name = "a") to PojoA, but I think it's not used when you have #XmlElements
"As I cannot modify the input XML, is there anything else I can do, either with Jackson, Jackson XML or JAXB annotations?"
I'm not too familiar with Jackson's Xml features. But with the JAXB Unmarshaller and a little change to your annotations, this can be achieved.
For your List<AbstractPojo> you can use the #XmlAnyElement(lax = true).
With lax = true:
If true, when an element matches a property marked with XmlAnyElement is known to JAXBContext (for example, there's a class with XmlRootElement that has the same tag name, or there's XmlElementDecl that has the same tag name), the unmarshaller will eagerly unmarshal this element to the JAXB object, instead of unmarshalling it to DOM
Which basically means that if we annotate PojoA and PojoB with #XmlRootElement and pass the element name as the name attribute (#XmlRootElement(name = "a")), by definition, this should work.
Let's give it a shot:
public abstract class AbstractPojo {
// note this class is not annotated. It will be known in the context
// as we're explicitly using the type int the Root class
}
#XmlRootElement(name = "a")
public class PojoA extends AbstractPojo {
}
#XmlRootElement(name = "b")
public class PojoB extends AbstractPojo {
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "root")
public class Root {
#XmlAnyElement(lax = true)
protected List<AbstractPojo> objects;
public List<AbstractPojo> getObjects() {
if (objects == null) {
objects = new ArrayList<>();
}
return this.objects;
}
}
Using the following xml file to test
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a/> <b/> <b/> <a/> <a/> <b/> <b/>
</root>
And the following test program
public class JaxbTest {
private static final String FILE_NAME = "test.xml";
public static void main(String[] args) throws Exception {
JAXBContext context = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
File f = new File(FILE_NAME);
Root root = (Root)unmarshaller.unmarshal(f);
List<AbstractPojo> list = root.getObjects();
for (AbstractPojo p : list) {
System.out.print( p instanceof PojoA ? "a " : "b ");
}
System.out.println();
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
We get the result we're looking for.
a b b a a b b // along with the xml file content we marshal for testing
Here are some good resources:
JAXB tutorial
Oracle JAXB tutorial
#XmlAnyElement
Blaise Doughan's blog is always a good resource
UPDATE
Ok I see why you were getting ElementNSImpl. This doesn't work for me, I'm getting ElementNSImpl objects. Do you know what's happening?
Yea I see what's happening. Firstly, I compiled an xsd with xjc, and it created an ObjectFactory for me, which declared the elements. That's why it way working for me.
If you don't do this, then you should explicitly put the PojoA and PojoB into the context.
JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class);
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).
First. Sorry for bad english.
I want to make some "common" transformation of Map to XML according to given XSD in that way:
key of the Map will be equal to tag name in XML
tag names in XML will not be duplicated in different nodes (levels)
value in Map can contain for example List of Map that represent repeatable tags in the node
created xml have to accord an xsd.
etc.
So I am looking for a competent way to realize that.
Is there anybody who worked with similar tasks and can help me?
Any advise will appreciated. Thanks in advance!
P.S. Example.
Map:
"fname" : "Asdf"
"lname" : "Fdsa"
"cars" : "car" {"car1", "car2", "car3"}
XML:
<fname>Asdf</fname>
<lname>Fdsa</lname>
<cars>
<car>car1</car>
<car>car2</car>
<car>car3</car>
</cars>
First, you need one single root element. This is the requirement of XML syntax.
Now you can use JAXB. Define you class Data:
#XmlType
public class Data {
private String fname;
private String lname;
private Collection<String> cars;
// getters
public String getFname() {
return fname;
}
public String getLname() {
return lname;
}
#XmlElementWrapper(name = "cars")
#XmlElement(name = "car")
public String getCars() {
return cars;
}
// setters.....
}
Now your can create instance of this class instance, call all setters to fill the data and then call:
JAXBContext ctx = JAXBContext.newInstance("com.yourpackage");
Marshaller m = ctx.createMarshaller();
m.marshal(data, System.out);
And you will see your data serialized as XML on STDOUT.
To parse XML back say:
JAXBContext ctx = JAXBContext.newInstance("com.panpwr.api.model.deployment");
Unmarshaller unmarshaller = ctx.createUnmarshaller();
Data data = (Data)unmarshaller.unmarshal(in); // in is the input stream for XML
// now your instance of data is populated from XML
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;
}