I'm looking for best tool/way to create and load JAVA objects from XML definitions.
I had checked out JAXB, seems pretty nice, but didn't find is there a way to work with Entities which properties are dynamic, or changed from time to time, so want to have something like automatic way of working with entities, without converting Object into predefine Entity object. Does something like that exists?
Workflow would be like this read from XML create class for each Entity with dynamic set of attributes and/or create ORM mapping part for those entities and then all manipulation retrieve/store into db or probably will going to use some NoSQL solution like MongoDB.
Note: I'm the EclipseLink JAXB (MOXy) lead, and a member of the JAXB 2 (JSR-222) expert group.
Check out the following EclipseLink example. It demonstrates how to use dynamic properties with both the JPA and JAXB implementations:
http://wiki.eclipse.org/EclipseLink/Examples/MySports
Option #1 - Static Objects with Dynamic Properties
MOXy has an #XmlVirtualAccessMethods extension which allows you to map entries in a map to XML. This allows you to add properties to a static class. In the example below the Customer class has a "real" name property and may have many "virtual" properties.
package blog.metadatasource.refresh;
import java.util.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlVirtualAccessMethods;
#XmlRootElement
#XmlType(propOrder={"firstName", "lastName", "address"})
#XmlVirtualAccessMethods
public class Customer {
private String name;
private Map<String, Object> extensions = new HashMap<String, Object>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object get(String key) {
return extensions.get(key);
}
public void set(String key, Object value) {
extensions.put(key, value);
}
}
The virtual properties are defined via MOXy's XML metadata. In the example below we will add two properties: middleName and shippingAddress.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="blog.metadatasource.refresh">
<java-types>
<java-type name="Customer">
<java-attributes>
<xml-element
java-attribute="middleName"
name="middle-name"
type="java.lang.String"/>
<xml-element
java-attribute="shippingAddress"
name="shipping-address"
type="blog.metadatasource.multiple.Address"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
For More Information
http://blog.bdoughan.com/2011/06/extensible-models-with-eclipselink-jaxb.html
http://blog.bdoughan.com/2011/06/moxy-extensible-models-multi-tenant.html
http://blog.bdoughan.com/2011/06/moxy-extensible-models-multiple.html
http://blog.bdoughan.com/2011/06/moxy-extensible-models-refresh-example.html
Option #2 - Dynamic Objects
MOXy also offers full dynamic object models:
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, null, null);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
DynamicEntity customer = (DynamicEntity) unmarshaller.unmarshal(inputStream);
DynamicEntity address = jaxbContext.newDynamicEntity("org.example.Address");
address.set(street, "123 A Street");
address.set(city, "Any Town");
customer.set("address", address);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(customer, System.out);
For More Information
http://wiki.eclipse.org/EclipseLink/UserGuide/MOXy
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/Dynamic
So, you're basically trying to make POJO's (plain old Java objects) using XML files? They are just like data classes, right?
I'm a big fan of XStream, which is really easy to use and works great if you don't need validation. I've used Castor when validation against a schema was required. I just used XStream to save an object to an xml file and then I can read it back in from anywhere, even if I change the data values associated with the object (which I think is what you mean by "dynamic set of attributes", right?).
Related
We have lot of hibernate entity classes with nested relationships. I'm trying to find best way to convert given entity to equivalent json format.
I know about JsonIgnore, Jackson mixins and JsonFilters and have been experimenting with those.
Challenges we are facing are as following
Objects related to each other using OneToMany/JoinColumn or similar annotations - creates infinite recursion.
Utility or meta methods. Jackson seems to be going by getter methods and not by fields. Some of the methods are "meta" method that is not associated with any columns. Example getTotal method can be summing of values of couple of actual fields without having actual total field. Similar case for other cases like getIncomeAccounts which filters accounts based on some criteria.
Jackson Filter I wrote helps a little - it checks if the class field exists based on Jackson property name. It also checks if there annotations such as JoinColumn annotation to avoid recursion if the field exists.
Is there any way I can get metadata from hibernate and use it in my filters? Basically for given entity object, I am interested in knowing if the property Jackson wants to serialize will be mapped to a column and serialize only if there is column value associated with it. Hibernate is certainly aware of properties and column mappings.
Mixins and jsonignore options are workable, but then we depend upon individual developer remembering about putting annotations at appropriate place. Usually forgotten annotations are discovered too late when we really want to get the exported data to analyze some problem and create test case locally.
What I usually do is map Entities to DTOs manually, or with the help of tools such as MapStruct.
This will give you maximum flexibility, with a bit of overhead at the beginning, unfortunately.
Over time however, you'll see it was worth it.
Jackson, GSON, and other serialization tools are obviously limited in what they can do out-of-the-box, and this kind of customization requires a bit too much work, imho, while also being difficult to understand and maintain.
Keep it simple.
If you do not want to create new POJO model for representing JSON on REST API level you need to prepare ORM model before passing to Jackson layer.
Enable HibernateXModule
You should start from enabling Hibernate module which fits the best to your Hibernate version. It solves many problem with lazy-loadings and internal data types.
Bidirectional Relationships
Read about options which Jackson have for solving cycles problem during serialisation. Main annotations are:
JsonManagedReference
JsonBackReference
JsonIdentityInfo
Define proper Visibility
You can define global visibility on ObjectMapper, how to specify jackson to only use fields - preferably globally and customise it if needed for given class using JsonAutoDetect annotation.
View POJO model
Probably for most cases you will be able to reuse POJO model created for ORM. In cases where customising JSON output with annotation will be to hard you can always create custom class and map ORM model to this one manually in extra mapping/business layer.
Customise serialisers
In case you need to handle some custom annotations or some fields in general way you can use BeanSerializerModifier and BeanPropertyWriter. It is not easy to implement but it is very powerful. See example usage here: Jackson custom serialization and deserialization.
Simple example how it could be done for bidirectional relations and visibility configuration:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
Item item0 = new Item();
item0.setId(1);
item0.setItemName("Item 0");
Item item1 = new Item();
item1.setId(2);
item1.setItemName("Item 1");
List<Item> items = Arrays.asList(item0, item1);
User user = new User();
user.setId(123);
user.setName("Rick");
user.setUserItems(items);
items.forEach(item -> item.setOwner(user));
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
System.out.println(mapper.writeValueAsString(user));
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class User {
private int id;
private String name;
private List<Item> userItems;
// getters, setters
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class Item {
private int id;
private String itemName;
private User owner;
// getters, setters
}
Above code prints:
{"id":123,"name":"Rick","userItems":[{"id":1,"itemName":"Item 0","owner":123},{"id":2,"itemName":"Item 1","owner":123}]}
I figure this will be easy for someone who really understands JAXB binding files...
Basic Question
How do you configure JAXB to unmarshal multiple elements into the same class?
Note: I want to avoid adding another dependency to my project (like MOXy). Ideally, this can be accomplished with annotations or a custom bindings file.
Background
I have an XML document that contains lots of variations of the same element--each with the exact same properties. Using my example below, all I care about is "Employees" but the XML specifies "directors, managers and staff." For our purposes, these are all subclasses of the same parent and we only need to work with the parent type (Employee) and our object model doesn't have or need instances of the subclasses.
I want JAXB to bind any instance of director, manager, or staff elements into an Employee object.
Example
input:
<organization>
<director>
<fname>Dan</fname>
<lname>Schman</lname>
</director>
<manager>
<fname>Joe</fname>
<lname>Schmo</lname>
</manager>
<staff>
<fname>Ron</fname>
<lname>Schwan</lname>
</staff>
<staff>
<fname>Jim</fname>
<lname>Schwim</lname>
</staff>
<staff>
<fname>Jon</fname>
<lname>Schwon</lname>
</staff>
</organization>
output:
After unmarshalling this example, I would end up with an Organization object with one property: List<Employees> employees where each employee only has a firstName and lastName.
(Note: each employee would be of type Employee NOT Director/Manager/Staff. Subclass information would be lost when unmarshalling. We also don't care about marshaling back out--we only need to create objects from XML)
Can this be done without extensions like MOXy? Can a custom bindings.xjb file save the day?
This corresponds to a choice structure. You could use an #XmlElements annotation for this use case:
#XmlElements({
#XmlElement(name="director", type=Employee.class),
#XmlElement(name="manager", type=Employee.class)
})
List<Employee> getEmployees() {
return employees;
}
If you are starting from an XML schema the following will help:
http://blog.bdoughan.com/2011/04/xml-schema-to-java-xsd-choice.html
How can I covert a an xml file to a simple java bean?
Its a simple xml file without any xsd, which was generated from a java bean, which I don't have access to.
I tried using xmlbeans to first generate the xmd from xml and then to generate classes from the xsd. I got a bunch of classes. I am looking for a single java bean class.
JAXB
JAXB (JSR-222) provides an easy way to convert objects to XML. There are many open source implementations of this standard including:
Metro JAXB (the reference implementation included in Java SE 6)
EclipseLink JAXB (MOXy), I'm the tech lead
Apache JaxMe
JAXB has a default mapping for Java objects to XML. This mapping can be customized through the application of annotations.
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.Element;
#XmlRootElement
public class Address {
private String street;
private String city;
private String state;
private String country;
#XmlElement(name="postal-code")
private String postalCode;
}
Would correspond to the following XML:
<address>
<street>123 A Street</street>
<city>Any Town</city>
<state>A State</state>
<postal-code>12345</postal-code>
</address>
EclipseLink JAXB (MOXy)
MOXy has an XPath based mapping extension. This means we can take our same Address class and map it to Google's geocode format:
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="kml")
#XmlType(propOrder={"country", "state", "city", "street", "postalCode"})
public class Address {
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:SubAdministrativeArea/ns:Locality/ns:Thoroughfare/ns:ThoroughfareName/text()")
private String street;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:SubAdministrativeArea/ns:Locality/ns:LocalityName/text()")
private String city;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:AdministrativeAreaName/text()")
private String state;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:CountryNameCode/text()")
private String country;
#XmlPath("Response/Placemark/ns:AddressDetails/ns:Country/ns:AdministrativeArea/ns:SubAdministrativeArea/ns:Locality/ns:PostalCode/ns:PostalCodeNumber/text()")
private String postalCode;
}
The above class corresponds to the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0" xmlns:ns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
<Response>
<Placemark>
<ns:AddressDetails>
<ns:Country>
<ns:CountryNameCode>US</ns:CountryNameCode>
<ns:AdministrativeArea>
<ns:AdministrativeAreaName>CA</ns:AdministrativeAreaName>
<ns:SubAdministrativeArea>
<ns:Locality>
<ns:LocalityName>Mountain View</ns:LocalityName>
<ns:Thoroughfare>
<ns:ThoroughfareName>1600 Amphitheatre Pkwy</ns:ThoroughfareName>
</ns:Thoroughfare>
<ns:PostalCode>
<ns:PostalCodeNumber>94043</ns:PostalCodeNumber>
</ns:PostalCode>
</ns:Locality>
</ns:SubAdministrativeArea>
</ns:AdministrativeArea>
</ns:Country>
</ns:AddressDetails>
</Placemark>
</Response>
</kml>
For more Information
XPath Based Mapping - Geocode Example
Map to Element based on an Attribute Value with EclipseLink JAXB (MOXy)
XPath Based Mapping
Try Castor Mapping.
You could use a tool like Castor or JAXB to map the XML to a java class. Castor is fairly easy to use.
When using JAXB with Java-First, fields/properties of type java.util.Date are marshalled and unmarshalled as xs:dateTime and everything works as expected.
But if the type of the field/property is Object, JAXB unmarshals xs:dateTimeto XMLGregorianCalendarImpl.
I need to find a way that makes JAXB unmarshal date-time values to java.util.Date by itself using annotations. Otherwise, I'll need to go through all unmarshalled values in each use case.
Even if there were some after-unmarshall-hook to define on the classes containing Object fields and convert the instances manually would be good. But I couldn't find anything that can be used this way either.
Note that I have limited access to the JAXB context, as it is used inside Apache CXF.
In addition to Blaise Doughan's answer:
I could finally figure this out, thanks for help from Blaise Doughan. Actually his answer works with just a small change: if there's several types expected to be unmarshalled as the Object property, there needs to be multiple #XmlElement annotations placed on it using #XmlElements annotation.
Here's my code now:
#XmlElements
({
#XmlElement(name = "dateValue", type = Date.class),
#XmlElement(name = "stringValue", type = String.class),
#XmlElement(name = "booleanValue", type = Boolean.class),
#XmlElement(name = "listValue", type = ArrayList.class),
#XmlElement(name = "bytesValue", type = Byte[].class)
})
public Object getFieldValue()
{
return fieldValue;
}
Note: specifying "name" is required for this to work, since there should be a way for the marshaller / unmarshaller to identify the type of the content.
There are two minor issues here:
You need to specify a list of all of the types expected (which is logical, given the case of marshalling)
There's no way to specify a single name for this property. In my case, where JAXB is used in CXF web services, code generated from WSDL in .NET names this field as "Item". If there was a way, for example, to wrap the XML elements in another one which has a single name, the generated code could be a little bit nicer.
You can set the type property on #XmlElement.
#XmlElement(type=Date.class)
public Object getGenericDateProperty() {
return date;
}
Edit:
Since you don't know the type you could use an XmlAdapter. If the unmarshalled value is an XMLGregorianCalendar convert it to a Date. For more info on XmlAdapter see:
http://bdoughan.blogspot.com/2010/07/xmladapter-jaxbs-secret-weapon.html
Note: Editing this to rephrase it around JAXB in hopes of getting new answers. I'm using CXF, but it's using JAXB for the mappings.
I've got a POJO model. Right now I have it mapped via annotations and using JAXB to spew/read XML. However that's only one XML format and I need to map that POJO model to one of various XML formats depending on the 3rd party system I'm integrating with (e.g. various 3rd parties all have the concept of a "person", but map it differently). I've read through the entire JAXB tutorial, but everything is centered around annotations. Is there some external way to map the classes so I can read/write multiple mappings where I pick the mapping to use at any given point (i.e. I know I'm spewing a "person" to Foo Inc., so use the foo mapping)?
Edit: I just found something called JAXBIntroductions that might do the job.
http://community.jboss.org/wiki/JAXBIntroductions
If you are using EclipseLink JAXB (MOXy) you can take advantage of the externalized mapping feature to apply many XML representations to your POJOs.
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/EclipseLink-OXM.XML
Also, since MOXy's mappings are XPath based you can actually map your POJOs to a wide variety of XML schemas.
you can use XStream for this. you can work without annotations like this:
XStream xstream = new XStream();
XStream xstream = new XStream(new DomDriver());
xstream.alias("person", Person.class);
xstream.alias("phonenumber", PhoneNumber.class);
hope that helps
EDIT: create another XStream instance for a different output
Person demo = new Person("Chris");
XStream xStream = new XStream();
xStream.alias("person", Person.class);
System.out.println(xStream.toXML(demo));
XStream xStream2 = new XStream();
xStream2.alias("dev", Person.class);
System.out.println(xStream2.toXML(demo));
output:
<person>
<name>Chris</name>
</person>
<dev>
<name>Chris</name>
</dev>
you can try http://code.google.com/p/jlibs/wiki/SAX2JavaBinding
This is also based on annotations. but annotations are not on your POJO.