Can marshalling and unmarshalling behave differently? - jaxb

With this object for example :
public class Person {
#XmlAttribute
private String name = null;
#XmlElement
private Address address = null;
// getters and setters
}
XML file :
<person name="blabla">
<address>...</address>
</person>
What can I do if I want to load a complete Person object (name + address) when umarshalling but only saving its name when marshalling ? (i.e. not saving the address)

The easiest would be to make a copy and remove unnecessary data.
Another (much more complicated option) option would be to defined different mappings. This is possible with MOXy:
Map<String, Object> tenantOneProps = new HashMap<String, Object>();
ArrayList tenantOneBindings = new ArrayList();
tenantOneBindings.add("examples/virtual/base-bindings.xml");
tenantOneBindings.add("examples/virtual/binding-tenant1.xml");
tenantOneProps.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, tenantOneBindings);
JAXBContext tentantOneContext = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, tenantOneProps);
Map<String, Object> tenantTwoProps= new HashMap<String, Object>();
ArrayList tenantTwoBindings = new ArrayList();
tenantTwoBindings.add("examples/virtual/base-bindings.xml");
tenantTwoBindings.add("examples/virtual/binding-tenant2.xml");
tenantTwoProps.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, tenantTwoBindings);
JAXBContext tentantTwoContext = JAXBContext.newInstance(new Class[] {Customer.class, Address.class}, tenantTwoProps);
Here you define two sets of mappings - base plus either tenant1 or tenant2. Thus you create two contexts. You can unmarshal with one and marshal with another.

Related

How to create edge label in Arangodb?

java-driver 4.1.10 I've created database named mydatabase1 I've created a graph using Java so my question is that how to set edge label using Java code?
If you mean with "edge label" a field in an edge-document, you can set it when you call insertEdge like in the code below.
ArangoDB arangoDB = new ArangoDB.Builder().build();
// create database
arangoDB.createDatabase("myDatabase");
// create named graph
Set<EdgeDefinition> edgeDefinitions = Collections.singleton(
new EdgeDefinition().collection("myEdgeCollection").from("myVertexCollection").to("myVertexCollection"));
arangoDB.db("myDatabase").createGraph("myGraph", edgeDefinitions);
// create from vertex
BaseDocument from = new BaseDocument("myFromKey");
arangoDB.db("myDatabase").graph("myGraph").vertexCollection("myVertexCollection").insertVertex(from);
// create to vertex
BaseDocument to = new BaseDocument("myToKey");
arangoDB.db("myDatabase").graph("myGraph").vertexCollection("myVertexCollection").insertVertex(to);
// create edge
BaseEdgeDocument edge = new BaseEdgeDocument("myVertexCollection/myFromKey",
"myVertexCollection/myToKey");
edge.addAttribute("label", "value");
edge.addAttribute("whatever", 42);
arangoDB.db("myDatabase").graph("myGraph").edgeCollection("myEdgeCollection").insertEdge(edge);
Instead of using BaseEdgeDocument you can also use Map<String, Object>
Map<String, Object> edge = new HashMap<String, Object>();
edge.put("_from", "myVertexCollection/myFromKey");
edge.put("_to", "myVertexCollection/myToKey");
edge.put("label", "value");
edge.put("whatever", 42);
arangoDB.db("myDatabase").graph("myGraph").edgeCollection("myEdgeCollection").in sertEdge(edge);
or create your own POJO representing your edge. The edge needs at least the fields _from and _to. If you don't want to name the fields in your POJO _from and _to, you can use the annotation DocumentField with the values Type.FROM and Type.TO on two String fields of your choice.
public class MyEdge {
#DocumentField(Type.FROM)
private String from;
#DocumentField(Type.TO)
private String to;
public MyEdge() {}
..setter & getter
}

JAXB unmarshalling doesn't work when field is declared as List but doesn't work when same field is declared as ArrayList

I recently started working on a code which contains some JAXB serializable/deserializable classes. One of the classes has a few lists in it and I wanted to add a new list to it. The lists were initially declared as ArrayList. Similarly, the get methods were also returning ArrayList. I changed all of them to List and added the new list as well as List. But after that, I was not able to unmarshal the xml for this object into a JAVA object. When I change the fields back to ArrayList without any other change, the unmarshalling works fine. I have also tried to attach the DefaultValidationEventHandler to the Unmarshaller but it doesn't spit out any error while unmarshalling. Below is how the class looks like with class and variable names change
#XmlRootElement(name = "commandSet")
public class CommandSet {
private final ArrayList<Command> xCommands;
private final ArrayList<Command> yCommands;
#Override
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand", type = Command.class)
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#Override
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand", type = Command.class)
public ArrayList<Command> getYCommands() {
return this.yCommands;
}
}
When xCommands and yCommands are declared as List and the getters also return List, the unmarhsalling doesn't work.
In all the examples that I have found for unmarshalling lists, people have used List<> instead of ArrayList<>. Any ideas why is it not working for me with List<>?
Things I Noticed About Your Code
There are a couple of weird things I notice about your code that may or may not factor into your problem. At the very least I suspect the model you are experiencing the problem with is different from the model you posted in your question.
You have the fields marked final, but never initialize them.
You annotated both of the get methods with #Override but since CommandSet doesn't inherit from anything (other than Object), you aren't overriding anything.
Complete Working Example
Java Model
CommandSet
In this version of the CommandSet class I have made one of the properties type ArrayList and the other of type List to demonstrate that they both work. I have also removed the weird things about your code mentioned above.
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class CommandSet {
private final ArrayList<Command> xCommands;
private final List<Command> yCommands;
public CommandSet() {
xCommands = new ArrayList<Command>();
yCommands = new ArrayList<Command>();
}
#XmlElementWrapper(name = "xCommands")
#XmlElement(name = "xCommand")
public ArrayList<Command> getXCommands() {
return this.xCommands;
}
#XmlElementWrapper(name = "yCommands")
#XmlElement(name = "yCommand")
public List<Command> getYCommands() {
return this.yCommands;
}
}
Command
public class Command {
}
Demo Code
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(CommandSet.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
CommandSet commandSet = (CommandSet) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(commandSet, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<commandSet>
<xCommands>
<xCommand/>
<xCommand/>
</xCommands>
<yCommands>
<yCommand/>
<yCommand/>
</yCommands>
</commandSet>

How flexible is JAXB?

I am considering using JAXB for XML parsing but I'm having a couple of issues so far that lead me to believe that it might not be flexible enough for what I want.
I'll be parsing XML that is provided by third parties to conform to an XSD that I'll publish. So I want to be flexible enough to handle files that don't have namespaces or specify an old version of the namespace and may in fact contain invalid elements.
Is this sort of flexibility possible with JAXB? At the moment it fails to parse if the namespace is not provided.
How flexible is JAXB?
Very
So I want to be flexible enough to handle files that don't have
namespaces or specify an old version of the namespace and may in fact
contain invalid elements.
NamespaceFilter
Below is a SAX XmlFilter that can be used to apply a missing namespace.
import org.xml.sax.*;
import org.xml.sax.helpers.XMLFilterImpl;
public class NamespaceFilter extends XMLFilterImpl {
private static final String NAMESPACE = "http://www.example.com/customer";
#Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
super.endElement(NAMESPACE, localName, qName);
}
#Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
super.startElement(NAMESPACE, localName, qName, atts);
}
}
Demo
Below is an example of how you can apply the SAX XMLFilter with JAXB.
import javax.xml.bind.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
public class Demo {
public static void main(String[] args) throws Exception {
// Create the JAXBContext
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Create the XMLFilter
XMLFilter filter = new NamespaceFilter();
// Set the parent XMLReader on the XMLFilter
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
filter.setParent(xr);
// Set UnmarshallerHandler as ContentHandler on XMLFilter
Unmarshaller unmarshaller = jc.createUnmarshaller();
UnmarshallerHandler unmarshallerHandler = unmarshaller
.getUnmarshallerHandler();
filter.setContentHandler(unmarshallerHandler);
// Parse the XML
InputSource xml = new InputSource("src/blog/namespace/sax/input.xml");
filter.parse(xml);
Customer customer = (Customer) unmarshallerHandler.getResult();
}
}
For More Information
http://blog.bdoughan.com/2012/11/applying-namespace-during-jaxb-unmarshal.html

How does MOXy handle empty arrays

In my Jersey project, I am using MOXy to marshal to/from JSON. One of the classes I want to to marshal is an array of strings that could be empty.
class Data
{
#XmlElement(nillable = true) public String[] array;
}
In the case where the array is empty, I would like the output to be:
{
"array" : []
}
however it looks like MOXy is filtering out empty arrays from the output. How can I get it to include empty arrays in the output?
I was thinking about adding JSONConfiguration.mapped().array("array").build() to the MOXy provider constructor, but this doesn't seem to make a difference (and I'm not even sure that it's the correct solution).
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
ORIGINAL ANSWER
The following enhancement request has been entered. You can use the link below to track our progress on this issue.
http://bugs.eclipse.org/378894
UPDATE
Starting with the May 19, 2012 EclipseLink 2.4.0 label you can set the following property to get the behaviour you are looking for.
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, true);
You can download nightly EclipseLink labels from the following location:
http://www.eclipse.org/eclipselink/downloads/nightly.php
Root
On the following class we have three List properties. Two are of the List objects are empty and one is null. Note that the emptyChoiceList field is mapped with #XmlElements. The #XmlElements annotation states that the possible node names are foo and bar.
package forum10453441;
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlType(propOrder={"nullList", "emptyList", "emptyChoiceList"})
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private List<String> nullList = null;
private List<String> emptyList = new ArrayList<String>();
#XmlElements({
#XmlElement(name="foo", type=String.class),
#XmlElement(name="bar", type=String.class)
})
private List<String> emptyChoiceList = new ArrayList<String>();
}
Demo
The following code demonstrates how to specify the MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS property.
package forum10453441;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
marshaller.setProperty(MarshallerProperties.JSON_MARSHAL_EMPTY_COLLECTIONS, true);
Root root = new Root();
marshaller.marshal(root, System.out);
}
}
Output
Now in the output the empty lists are marshalled as empty arrays. For the field that was mapped with #XmlElememts the node name that was specified was used in the JSON representation.
{
"emptyList" : [ ],
"foo" : [ ]
}

MOXy JAXB: how to exclude elements from marshalling

I have my model:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class CustomerTest {
private Long id;
#XmlPath("contact-info/billing-address")
private AddressTest billingAddress;
#XmlPath("contact-info/shipping-address")
private AddressTest shippingAddress;
#XmlPath("FileHeader/SchemaVersion/text()")
private String schemaVersion;
}
And I fill in the object like this:
private void marshallCustomerTest() {
try {
JAXBContext jc = JAXBContext.newInstance(CustomerTest.class);
CustomerTest customer = new CustomerTest();
customer.setId(new Long(10));
customer.setSchemaVersion("3.2");
AddressTest billingAddress = new AddressTest();
billingAddress.setStreet("1 Billing Street");
customer.setBillingAddress(billingAddress);
AddressTest shippingAddress = new AddressTest();
shippingAddress.setStreet("2 Shipping Road");
customer.setShippingAddress(shippingAddress);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
} catch (JAXBException jex) {
jex.printStackTrace();
log.error(jex);
}
}
This produce the next XML:
<customerTest xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<id>10</id>
<contact-info>
<billing-address>
<street>1 Billing Street</street>
</billing-address>
<shipping-address>
<street>2 Shipping Road</street>
</shipping-address>
</contact-info>
<FileHeader>
<SchemaVersion>3.2</SchemaVersion>
</FileHeader>
</customerTest>
As you can see there is not #XmlPath annotation for 'id' property but this is also present in the final XML. I know I can avoid this behaviour setting the 'id' property to null but I want to know if there is another way. The point is that my real model is much bigger than this one and I would have to set a lot of properties to null.
Any help?
Thanks in advance.
You can either mark the property with #XmlTransient to have it excluded from the XML representation:
#XmlTransient
private Long id;
Or you can annotate your type with #XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties are mapped.
#XmlAccessorType(XmlAccessType.NONE)
public class CustomerTest {
For More Information
http://blog.bdoughan.com/2012/04/jaxb-and-unmapped-properties.html

Resources