How would you unmarshall the following with JAXB - java

How would one unmarshall the following XML response with JAXB into a domain class:
<?xml version="1.0" encoding="UTF-8"?>
<time>2014-01-14T06:24:34+00:00</time>
My first instinct was to use something like (short version):
#XmlRootElement
public class Time {
#XmlElement
public Date time;
}
but I think JAXB then sees 2 elements with the Time name. I also tried without using the #XmlRootElement annotation, but to no avail.

Have you tried using #XmlValue instead of #XmlElement for the time field? After all, it is the value of the root element, rather than a sub-element.
I've now tried this with the file supplied, and it works properly.

Related

Use JAXB Annotation With Value

When generating XML using JAXB annotations, I know it's not possible to use #XmlElement(name="City") & #XmlValue on the same Java member because they are mutually exclusive. Is it possible to #XmlElement to produce an XML tag with a value at the same time? Not being able to do this causes a ton of objects to be created and seems to be overkill.
Java Code
....
#XmlElement(name="City")
#XmlValue <---- I'm wanting to do this but I'm limited by the API
private String city;
Expected Output
....
<City>some value here</City>
....
We can try to achieve the same using another type which uses #XmlValue annotation.
Below is what you can try -
#XmlRootElement(name="CityRoot")
#XmlType(name="CityRootType")
public class CityRoot {
#XmlElement(name="City")
public CityName s;
}
CityName definition as below
public class CityName {
#XmlValue
String name;
}
Now, feed these two files to schemagen to have the .xsd file generated and using that generate .xml file to verify.
Below is how generated xml file looks like when I generated it -
<?xml version="1.0" encoding="UTF-8"?>
<CityRoot>
<City>SomeCityName</City>
</CityRoot>
If you want to have an element with simple text, the only annotation you need is the #XmlElement annotation. If the type of a field is String, JAXB generates an xml element with the value of the String as the value of the element.
The only thing you need is this:
#XmlElement(name="City")
private String city;

Annotation of XML Schema in Java

I'm working on an xml standard that requires that the following root element must be defined:
<ClinicalDocument xsi:schemaLocation=”urn:hl7 org:v3 CDA.xsd” xmlns=”urn:hl7-
org:v3” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
Now, I'm using java.xml.bind. Usually I annotate each class and then I use Marshallers and Unmarshallers to write/read valid xml files.
"My idea" was to annotate the package-info.java to specify the xsi:schemaLocation, xmlns and xmlns:xsi properties of ClinicalDocument. However, I can only insert the last property (xmlns:xsi), while I have no idea of how to render the first and furthermore the second is rendered as xmlns:ns3.
Here is my code in package-info.java:
#javax.xml.bind.annotation.XmlSchema (
xmlns = {
#javax.xml.bind.annotation.XmlNs(prefix="",
namespaceURI="urn:hl7-org:v3"),
#javax.xml.bind.annotation.XmlNs(prefix="xsi",
namespaceURI="http://www.w3.org/2001/XMLSchema-instance")
}
)
package foo;
Here is my class ClinicalDocument.java in package foo:
package foo;
#XmlRootElement(name="ClinicalDocument")
public class ClinicalDocument {....}
And finally is what I get with the Marshaller:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ClinicalDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns3="urn:hl7-org:v3">
...
</ClinicalDocument>
So, I have to create and read valid xml file under the three properties shown above. Any idea?
The only valid solution that I found is to add:
#XmlAttribute(name="xsi:schemaLocation")
protected final String xsi_schemaLocation="urn:hl7 org:v3 CDA.xsd";
#XmlAttribute(name="xmlns")
protected final String xmlns="urn:hl7-org:v3";
#XmlAttribute(name="xmlns:xsi")
protected final String xmlns_xsi="http://www.w3.org/2001/XMLSchema instance";
in class ClinicalDocument.
It works, but I don't like it! I would like to use annotation at package level.
Supporting the annotations is only the beginning of the requirements for reading and writing CDA documents - I would recommend the use of MDHT, open source project with an API to create, consume and validate CDA documents.
You can find the project here
https://www.projects.openhealthtools.org/sf/projects/mdht/

Handling different object types contained within the same set of elements

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.

JAXB marshal Set<Object>

I have an object similar to this:
public class Obj {
#XmlElement(name="value")
public Set<Object> values;
}
When marshaling, this is generating an xml like:
<Obj>
<value xsi:type="xs:dateTime" xmlns:xs="http://www.w3.org/2001/XMLSchema">2009-02-14T00:31:30.001+01:00</value>
<value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">test</value>
</Obj>
However, I want to change some of that values (like the date format used for serializing Date and Timestamp objects), and also get rid of the annoying xsi attributes (but this is not really a requirement, I can live with that)
I've tried adding a #XmlJavaTypeAdapter to values, but in the adapter I get the full Set<Object> to adapt, instead of single elements. I've also tried with a package adapter, but, as my Set is for Object, I cannot put the #XmlJavaTypeAdapter(type) attribute.
Also, I've tried with #XmlJavaTypeAdapter(value=MyAdapter.class, type=Timestamp.class) to get only an adapter for the values inside that Object that I want.
So the question is, does someone know a way to get an adapter to work for this? Or maybe, change the date format every time a Date or Timestamp object is serialized?
Thanks in advance!
#XmlJavaTypeAdapter with the type property has to be specified on the package level. When used in this way it indicates that all usages of that type within the specified package are converted using the XmlAdapter. E.g. if you have a package-info.java like
#XmlJavaTypeAdapters({
#XmlJavaTypeAdaptor(type=Timestamp.class, value=MyAdapter.class)
})
package org.example;
Then a class in that package with a Timestamp field.
package org.example;
public class Obj {
public Timestamp aTimestamp;
}
The specified adapter will be used to convert the timestamp. I suspect that this will work for your Set<Object> case but I haven't tried it myself.
The reason for the xsi:type attribute is that JAX-B likes to produce XML it can deserialize, so it needs to indicate what type it is or it could only parse everything back as strings. You can get rid of this attribute by using the #XmlElementRef annotation to create a schema substitution group, but in this case the XML will be produced with different element names. E.g.
public class Obj {
#XmlElementRefs({
#XmlElementRef(type=String.class, name="string"),
#XmlElementRef(type=Timestamp.class, name="timestamp")
})
public Set<Object> value;
}
Would produce the following XML structure if you had a timestamp and a string in the set. In this scenario the xsi:type attribute is unnecessary since JAX-B can tell what type to create from the element name.
<Obj>
<timestamp>2009-02-14T00:31:30.001+01:00</timestamp>
<string>test</string>
</Obj>
I would strongly recommend using the #XmlElementWrapper annotation to wrap up all the set items if you're going to take this approach.
If all you're after is a simple set of strings that you don't care about deserializing back to Java (or any other) objects with the correct types, then the simplest solution is to have an XmlAdapter that does just adapt the full Set<Object> into a Set<String> and handle the conversion yourself.

What is the Jaxb equivalent of a Text node value?

I am looking to convert a class that looks like this ...
public class Amenity {
public String id;
public String value;
}
into the following XML using JaxB annotations:
<amenity id="id-string-here">value-string-here</amenity>
Does anyone know what annotation to use on the value member variable to accomplish this? The closest I've gotten so far is:
#XmlRootElement
public class Amenity {
#XmlAttribute
public String id;
#XmlElement
public String value;
}
Unfortunately this approach doesn't allow me to specify that the value member variable should not be rendered as its own tag <value></value>.
I'm not 100% sure about this, but try to use an #XmlValue annotation instead of #XmlElement.
It looks like the question was referring to text nodes not CDATA nodes, but here is a link on how EclipseLink JAXB (MOXy) handles CDATA:
http://bdoughan.blogspot.com/2010/07/cdata-cdata-run-run-data-run.html
This documentation writes:
Q. How can I cause the Marshaller to generate CDATA blocks?
A. This functionality is not available from JAXB directly, but you can configure an Apache Xerces-J XMLSerializer to produce CDATA blocks. Please review the JaxbCDATASample.java sample app for more detail.
(btw, this does not answer your particular question, but since the question title is misleading, and this is the first google result for jaxb CDATA, I'm answering a bit different question)
JAXB does not support marshaling/marshaling to/from CDATA xml types.

Categories