I've got an XMLGregorianCalendar object that I'm trying to marshal into an xml string. I received this object by unmarshalling another xml object. Both are of type "dateTime", so they should be exactly the same...
And yet, when I marshal it, it shows up blank in the xml.
To illustrate this issue, I stripped everything down to the bare bones and made it generic in this example here. 2 java files, copy, paste, run as-is. The output you should receive would be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TheObject>
<DOB>2016-09-16</DOB>
</TheObject>
But, alas, it returns:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TheObject>
<DOB></DOB>
</TheObject>
Note: in the pastebin example, I create a xmlGregorianCalendar on the fly rather than grab one from another object like the code below, so technically it's not the same thing, but I think ultimately it illustrates the exact same issue... correct me if I'm wrong...
To add more context to my specific issue:
//Here are the objects themselves (names changed to protect the innocent)
//complete with annotations...
public class Object1{
...
#XmlElement(name = "DOB")
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar dob;
...
}
public class Object2{
...
#XmlElement(name = "DOB")
#XmlSchemaType(name = "dateTime")
protected XMLGregorianCalendar dob;
...
}
//and here's the snippet where the date object(date of birth) gets set
//from one object to another.
object2.setDOB(object1.getDOB());
//and finally, marshalling it to an xml string
private String marshallTheObject(Object2 theObject) throws JAXBException{
JAXBContext jaxbContext = JAXBContext.newInstance(Object2.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(object2, sw);
String output = sw.toString();
return output;
}
//the xml output shows: <DOB></DOB> instead of the date
I'm using the jaxb version that java 8 comes bundled with...
So my question is: is this some kind of bug? And if not, what am I doing wrong? How can I get around this issue without having to modify the generated java code? I cannot edit the xsd file used to generate it either...
Edit: for reference, the xsd file lists the DOB as:
<xs:element name="DOB" type="xs:dateTime" />
While I wasn't allowed to modify the xsd file directly, what I didn't realize was that I could change how the classes themselves were generated through a bindings file. I wasn't using maven in this case, so it was a bit difficult to determine the solution. I was generating the classes by using a feature built into eclipse that generates jaxb classes from an xsd file.
I learned more about bindings files here
I'm not exactly sure where that file needs to be placed in respect to maven, but in doing it the eclipse way it doesn't matter - you get to specify where the bindings file is during the jaxb class generation wizard.
Once generated, you'll need to code your own xml adapter.
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class CalendarDateTimeAdapter extends XmlAdapter<String, Date> {
//Sadly my specific situation requires me to strip the time off of all dateTime objects
//It's bad, but I didn't get to design the system, so this is the best compromise...
private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
#Override public Date unmarshal(String value) throws ParseException {
synchronized (sdf){
return sdf.parse(value);
}
}
#Override public String marshal(Date value) {
if(value == null) { return null; }
synchronized(sdf){
return sdf.format(value);
}
}
}
Make sure your classes match what you specified in your bindings file...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jaxb:bindings>
<jaxb:bindings version="2.1"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc">
<!-- Prevent wrapping data types into JAXBElements. -->
<jaxb:globalBindings generateElementProperty="false">
<!-- Use java.util.Date instead of XMLGregorianCalendar. -->
<xjc:javaType name="java.util.Date" xmlType="xs:dateTime"
adapter="com.package.location.adapter.CalendarDateTimeAdapter"/>
<xjc:javaType name="java.util.Date" xmlType="xs:date"
adapter="com.package.location.adapter.CalendarDateAdapter"/>
<xjc:javaType name="java.util.Date" xmlType="xs:time"
adapter="com.package.location.adapter.CalendarTimeAdapter"/>
</jaxb:globalBindings>
</jaxb:bindings>
Then, when doing your marshalling, use the setAdapter function to use it.
private String marshallObject(MyObject myObject) throws JAXBException{
JAXBContext jaxbContext = JAXBContext.newInstance(MyObject.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.setAdapter(new CalendarDateTimeAdapter());
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(myObject, sw);
String output = sw.toString();
return output;
}
Now they'll resolve to Date objects instead of XMLGregorianCalendar objects. And apparently, Date objects work better than XMLGregorianCalendars...
I'm still perturbed that the unmarshalled xml that went in doesn't marshal to become the same xml going out, but at any rate, this is precisely what I did to make it all start working. I'm sure I've done something against convention here, and if I have, please let me know.
Again I remind readers that I'm not using maven, nor any kind of framework (e.g. SpringMVC).
JAXB doesn't like that you're setting the time to DatatypeConstants.FIELD_UNDEFINED for a dateTime. If you comment out that line:
// calendar.setTime(DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED, DatatypeConstants.FIELD_UNDEFINED);
it will marshal something like:
<DOB>2016-09-16T00:00:00.000</DOB>
or you can change the generated annotation (or used customized bindings) to change dateTime to date like:
#XmlSchemaType(name = "date")
protected XMLGregorianCalendar dob;
if you want to see:
<DOB>2016-09-16</DOB>
If you want to go deeper, something like this may be a starting point, or maybe that's all you need.
Related
I'm trying to rewrite some API serialization from custom mappers to annotation-based style and faced with one hard mapping (which was earlier custom-serialized to json and xml separately) that I can not "translate" to. (Serialization is made with Jackson.)
In the POJO we have a collection, e.g.
class Data {
Set<Integer> tags;
}
which should be serialized in xml like:
<tags>
<tag id="1"/>
<tag id="2"/>
</tags>
and in json like:
{
"tags":[1,2]
}
Strait method with
#XmlElementWrapper(name="tags")
#XmlElement(name="tag")
gives good json, but incorrect xml like
<root>
<tags>
<tag>1<tag/>
<tag>2<tag/>
</tags>
</root>
cause there is no attribute specification.
I tried to wrap a bit with:
class Data{
#XmlElementWrapper(name="tags")
#XmlElement(name="tag")
Set<Tag> tags;
}
class Tag{
#XmlAttribute(name="id")
Integer id;
}
But this produces unwanted key in json format, like:
"tags":[
{"tag":{"id":1}},
{"tag":{"id":2}}
]
Ok, then. I tried to specify custom json serializer(implementing JsonSerializer and injecting with #JsonSerialize(using = ...) ), but seems it also affects xml "render".
Is it possible to do the trick with annotations only? Or mb is it possible somehow use default json serialization and custom xml serializtaion for some class? .e.g.
use custom xml serialization only for Reasons class in such way
class Data {
#XmlElement("tags")
Reasons tags;
}
but let all surrounding data be "render" with general strategy.
Simply create a getter annotated with #JsonValue will tell Jackson to produce a single value, without any field name.
This mapping:
#XmlRootElement
public class Data{
public Set<Tag> tags;
}
public class Tag{
#XmlAttribute
public Integer id;
#JsonValue
public Integer getId() {
return id;
}
}
Will then produce:
{"tags":[2,1]}
And:
<data><tags id="2"/><tags id="1"/></data>
PS: You're using JAXB annotations, i don't think Jackson will honor them.
To get the XML result above, you need to use JAXB:
StringWriter writer = new StringWriter();
JAXBContext context = JAXBContext.newInstance(Data.class, Tag.class);
Marshaller m = context.createMarshaller();
m.marshal(value, writer);
System.out.println(writer.getBuffer().toString());
As a final note, i'm not super fan of using a single mapping for multiple representations. It's probably good for simple stuff, but if your code grows more complex than this, i strongly suggest you create two sets of classes (one for XML mapping and one for JSON), maybe with a common interface.
I have an XML document that looks something like the following:
Note that I cannot change the schema because it is part of a standard XML Schema (Library of Congress METS).
<amdSec ID="AMDSEC001">
<digiprovMD ID="DMD001">
<mdWrap MDTYPE="OBJECT">
<xmlData>
<object xsi:type="file">
.....
</object>
</xmlData>
</mdWrap>
</digiprovMD>
<digiprovMD ID="DMD001_EVENT">
<mdWrap MDTYPE="EVENT">
<xmlData>
<event xsi:type="event">
.....
</event>
</xmlData>
</mdWrap>
</digiprovMD>
</amdSec>
As you can see, the inner element <mdWrap> can contain elements of different types; in this case they're <event> and <object>, but it isn't constrained to just those two types. Creating two classes (like below), marshals okay, but this doesn't work for unmarshalling.
class ObjectMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "object")
List<MyObject> object; //Wrapped in list to use #XmlElementWrapper
}
class EventMDWrap {
#XmlElementWrapper(name = "xmlData")
#XmlElement(name = "event")
List<MyEvent> event; //Wrapped in list to use #XmlElementWrapper
}
What can I do so that JAXB unmarshals the correct "type" of MDWrap?
I think, the best solution in this case is a generating POJO classes using XJC tool.
Download XSD file which describe XML file.
Using XJC tool convert XSD file into POJO classes. If XSD is not correct - fix it.
Make some changes if you need in generated classes.
Use this classes in marshalling / unmarshalling process.
I was able to figure out the solution, and it's much simpler than I initially thought (which speaks to my relative inexperience with XML and JAXB). By creating my MDWrap class in the following way
class MDWrap {
#XmlAnyElement(lax = true)
#XmlElementWrapper(name = "xmlData")
Object wrappedMD;
}
Then MDWrap can contain an object of any type, and will unmarshal properly, as long as the class of which wrappedMD is an instance of is annotated with #XmlRootElement. The trick is to annotate wrappedMD as XmlAnyElement.
i'm reading an xml file which contain the attributes of many objects of a Class; I'm using a DOM xml parser; in this Class there is also an array field (NEARBOXID), so I would know if is a good way to read it from the xml file like a single String and then split its content, or is there any better way to do this?
the file is like this:
<CONFIGURATION>
<CONFIG>
<BOXID>1</BOXID>
<LENGTH>100</LENGTH>
<NEARBOXID>2,3,4,5</NEARBOXID>
</CONFIG>
<CONFIG>
<BOXID>2</BOXID>
<LENGTH>200</LENGTH>
<NEARBOXID>1,8</NEARBOXID>
</CONFIG>
You should read as string and split it. Using a loop convert the numbers into integer using ParseInt
No, it's up to you to split that field. The String.split method will do it just fine.
You need to extract the complete data within the required tag using XPath, and later use String.split(), to get the desired values out of the complete string.
Since you are converting your XML to Java objects, I will demonstrate how this could be done using a JAXB (JSR-222) implementation. A JAXB implementation is included in the JDK/JRE starting with Java SE 6.
I would recommend changing the contents of the NEARBOXID element to be space separated.
<NEARBOXID>2 3 4 5</NEARBOXID>
The corresponds to the following entry in an XML schema. This means that you could validate that the element contains space separated int values instead of space separated Strings.
<xs:element name="NEARBOXID" minOccurs="0">
<xs:simpleType>
<xs:list itemType="xs:int"/>
</xs:simpleType>
</xs:element>
Config
Then you could map the element using JAXB's #XmlList annotation (see: http://blog.bdoughan.com/2010/09/jaxb-collection-properties.html).
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Config {
#XmlElement(name="BOXID")
private int boxId;
#XmlElement(name="LENGTH")
private int length;
#XmlElement(name="NEARBOXID")
#XmlList
private int[] nearBoxIds;
}
Configuration
The object below would map to the root of your XML document.
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement(name="CONFIGURATION")
#XmlAccessorType(XmlAccessType.FIELD)
public class Configuration {
#XmlElement(name="CONFIG")
private List<Config> configs;
}
Demo
Below is some demo code to prove that everything works.
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Configuration.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14305301/input.xml");
Configuration configuration = (Configuration) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(configuration, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<CONFIGURATION>
<CONFIG>
<BOXID>1</BOXID>
<LENGTH>100</LENGTH>
<NEARBOXID>2 3 4 5</NEARBOXID>
</CONFIG>
<CONFIG>
<BOXID>2</BOXID>
<LENGTH>200</LENGTH>
<NEARBOXID>1 8</NEARBOXID>
</CONFIG>
</CONFIGURATION>
I think it is a matter of choice. You can use it that way as:
<NEARBOXID>2,3,4,5</NEARBOXID>
or you use it that way:
<NEARBOXID>2</NEARBOXID>
<NEARBOXID>3</NEARBOXID>
<NEARBOXID>4</NEARBOXID>
<NEARBOXID>5</NEARBOXID>
Second way requires you to have different parsing code than the first one, but you can save some code on splitting and dealing with string.
Extract the data as a String and perform split() on it.
Better approach for it is to maintain in individual tags like
<NEARBOXID>2</NEARBOXID>
<NEARBOXID>3</NEARBOXID>
<NEARBOXID>4</NEARBOXID>
<NEARBOXID>5</NEARBOXID>
For parsing the xml query you can use XPath, XQuery or JAXB depending on your requirement.
I currently use JAXB for a project i'm working on and looking to convert my libraries archived xml to archived json, To act in my project. I figured I would use Jettison as it seems it would be easier to implement since it actually works with JAXB; however, looking at Older benchmarks in which Jettison was not included I have found Kryo produces smaller files and Serialized and DeSerialized quicker than some alternatives.
Can anyone inform me of the key difference or otherwise how Jettison stacks up to Kryo, especially for future projects such as android applications.
EDIT:
I guess i'm looking for what produces smaller files and operates faster. Human readability can be sacrificed since I don't plan on reading the files only processing them
They are for somewhat different purposes:
Jettison is for reading/writing JSON. Use it if you need to interoperte with a JSON (human-readable) data format
Kryo is for efficient binary serialisation. Use it if you need high performance and small encoded object sizes (e.g. communication of messages in a realtime game).
Since it sounds like you are using the format to archive data, human-readability and the use of a standard long-lived format is probably more important than efficiency, so I suspect you will want to choose the JSON route.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Since you already have established JAXB mappings and are converting XML to JSON, you may be interested in EclipseLink JAXB (MOXy) which offers both object-to-XML and object-to-JSON mapping using the same JAXB metadata.
Customer
Below is a sample model with JAXB annotations.
package forum11599191;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
#XmlAttribute
private int id;
private String firstName;
#XmlElement(nillable=true)
private String lastName;
private List<String> email;
}
jaxb.properties
To use 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
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<customer id="123" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<firstName>Jane</firstName>
<lastName xsi:nil="true"/>
<email>jdoe#example.com</email>
</customer>
Demo
The following demo code will populate the objects from XML and then output JSON. Note how there are no compile time dependencies on MOXy.
package forum11599191;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
// Unmarshal from XML
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11599191/input.xml");
Customer customer = (Customer) unmarshaller.unmarshal(xml);
// Marshal to JSON
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(customer, System.out);
}
}
JSON Ouput
Below is the output from running the demo code.
{
"customer" : {
"id" : 123,
"firstName" : "Jane",
"lastName" : null,
"email" : [ "jdoe#example.com" ]
}
}
A few things to note about the output:
Since the id field is a numeric type it was marshalled to JSON without quotes.
Even though the id field was mapped with #XmlAttribute there are no special indication of this in the JSON message.
The email property had a List of size one, this is properly represented in the JSON output.
The xsi:nil mechanism was used to specify that the lastName field had a null value, this has been translated to the proper null representation in the JSON output.
For More Information
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling-null.html
http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html
We are using hibernate to load data from oracle database. I need to load the data from one of the tables and then store selected data from this table as an xml file in another table. It would be great if someone could suggest, what would be the best way to achieve this functionality?
Take a look at this question which discusses libraries (such as JAXB and XStream) you can use to convert Java objects to XML.
What is the best way to convert a java object to xml with open source apis
Use Xstream of ThougthWorks.
"XStream is a simple library to serialize objects to XML and back again."
XMLEncoder (java.beans.XMLEncoder) is already included in the JDK and enables you to persist java objects to XML without any external libraries or tools.
An example class:
public class Foo {
private String foo ;
public void setFoo(String s) {
foo = s;
}
public String getFoo() {
return foo;
}
}
Helper class to serialize to XML:
import java.beans.XMLEncoder;
import java.io.*;
public class FooHelper {
public static void write(Foo f, String filename) throws Exception{
XMLEncoder encoder =
new XMLEncoder(
new BufferedOutputStream(
new FileOutputStream(filename)));
encoder.writeObject(f);
encoder.close();
}
}
The resulting XML:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.5.0-beta" class="java.beans.XMLDecoder">
<object class="Foo">
<void property="foo">
<string>bar</string>
</void>
</object>
</java>
If you are using JPA to persist your entities then look at if you can switch your provider to EclipseLink. If can convert the the same JPA persistent pojos to XML for you. So you will only have to deal with single library.
You can take a look at xmlbeans