I'm facing an issue using jaxb, when unmarshalling my xml, I can't manage to make the difference between one of my classes and an inherited one.
I got a 1 st class:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlClassExtractor(PersonExtractor.class)
#XmlSeeAlso(Toto.class)
public class Person {
}
An inherited one:
public class Toto
extends Person
{
#XmlElement(name = "additionalInformation")
private String additionalInformation;
}
My extractor:
public class PersonExtractor
extends ClassExtractor
{
#Override
public Class extractClassFromRow(Record pArg0, Session pArg1)
{
if (pArg0.get("additionalInformation") != null || pArg0.get("#additionalInformation") != null)
{
return Toto.class;
}
else
{
return Person.class;
}
}
}
Unfortunately this doesn't works.
When I'm trying to unmarshall Person.class is always returned.
If I change:
#XmlElement(name = "additionalInformation")
into
#XmlAttribute(name = "additionalInformation")
Everything works fine (unfortunately I do want an element).
None of those classes is my root element (everything is "deep" into my model)
Does someone know what I am missing ? probably somthing really dumb, but can't manage to put my hand on it.
Thanks
Probably you have a namespace define for your root element (or default namespace), so the element name is not additionalInformation but namespace:additionalInformation (or however it is represented in the Record element). Have you also checked that content of Toto class is saved correctly witht he additionalInformation element (I guess it was since it works with attribute, but just in case).
You can printout the content of your Record element to see what and how is stored there.
Thanks for the answer, I found a solution:
Apparently currently MOXy requires that the inheritance indicator is in an XML attribute for the #XmlClassExtractor to work.
I ended up finding and using an other way to handle inheritance using #XmlAdapter
Related
I have a simple enum I'd like to serialize and deserialize. The class looks like this:
public enum TipusViatge {
OCI,
NEGOCIS,
FAMILIA;
#Override
public String toString() {
return name().toUpperCase();
}
}
The thing is, I send it via a restful call and the receiving side may receive any type (so it only knows it will receive Object). So Jackson should be able to figure out the type of the argument to deserialize it.
Is it possible to do so? I was thinking that including the class name in the resulting json should allow Jackson to figure out the type, but I've been unable to do so.
I have worked over this problem for a while.
1st you could deserialize your json with Map<String, Object>. It alway works; you get standard types (your enumeration will be readed as plain string).
2nd in general case you alway know what kind of object you read. This is top-level object and you can set it to Jackson mapper: mapper.readerFor(cls).readValue(json). In case of your enumeration is a part of this cls object, then Jackson knows the type and just read value and parse to it.
3rd you actually could have multiple objects for one json string. I am talking about inheritance. And you could look at #JsonTypeInfo in Jackson documentation.
4th imagin that you read a json source and do not know what you read. In this case, you could ask Jackson to write marker at the beginning of the object. Just like you asking about class name. I think it relates to #JsonRootName. You can look on it here: Jackson JSON Deserialization with Root Element
I think that it is clean now how to work with objects in Jackson. I mean that we know how to tell Jackson what element we want to deserialize. Now we have one problem: how to serialize json -> our enumeration.
5th this is not a problem and works out of the box. Jackson uses name() method to serialize enumeration, and valueOf() to deserialize. You can look at it closer in EnumDeserializer in Jackson.
6th I do not like this behaviour, becuase it is case-sencitive. I faced with situation that when people write json string manually, the use lower-case and cannot deserialize it. Moreover, I belive, that writing enumeration constants directly to the json file is a bad practise, because If I want to refactor names of the enumeration, all existed json string should be modified as well (brrr). To solve thiese issues, I do following trick:
1. Implement EnumId interface with default implementation of parseId(String id) with using getId() to identify enumeration constants and using ignore case for compare.
1. I add id field to the enumeration
2. Add getId() - for serialization
3. Add parseId(String id) - for deserialization
4. Add new module in Jackson ObjectMapper with my customer serializer (it
should use `getId()` instead of `name()`).
if (enumId != null) {
generator.writeString(enumId.getId());
}
And tell Jackson how to deserialize this enum. Here this is dificult situation, becuase in different sources, Jackson use different deseriaization hierarchy and just adding another module to ObjectMapper with custom deserialize (just like in 4.) will not be working with all situations. To solve this problem, I found out that we could add #JsonCreator to parseId(String id) method in enumeration and Jackson will be using it in all situation.
I think that is all about this topic. I give you a code example to make it more clearly (it is better to write once, then explain twice):
public interface EnumId {
String name();
default String getId() {
return name().toLowerCase();
}
static <T extends Enum<?> & EnumId> T parseId(Class<T> cls, String id) {
T res = parseId(cls.getEnumConstants(), id, null);
if (res != null) {
return res;
}
throw new EnumConstantNotPresentException(cls, id);
}
static <T extends EnumId> T parseId(T[] values, String id, T def) {
for (T value : values) {
if (id != null ? id.equalsIgnoreCase(value.getId()) : value.getId() == null) {
return value;
}
}
return def;
}
static <T extends EnumId> T get(T value, T def) {
return value != null ? value : def;
}
}
public enum TipusViatge implements EnumId {
OCI,
NEGOCIS,
FAMILIA;
#JsonCreator
public static TipusViatge parseId(String id) {
return EnumId.parseId(TipusViatge.class, id);
}
}
This is in grails 2.5.6 code. I have a domain class that uses inheritance. One of the subclasses contains a list of strings stored in the variable values. When calling .save(), the domain class itself saves correctly with the right inheritance behavior, but the values do not get saved. Here is my domain classes:
abstract class Condition implements ICondition, IMarshaler {
String field;
static mapping = {
tablePerHierarchy false;
}
...
}
class ListCondition extends Condition {
static hasMany = [values: String];
List<String> values;
...
}
Attempting to save a new list condition and the getting it again from the database shows that there is no values.
ListCondition condition = new ListCondition(field: 'someField', values: ['test', 'otherTest'])
condition.save()
println ListCondition.getAll()[0].values.size() // Prints 0
Stumbled upon a similar issue. Try condition.save(flush: true) or even better try running your persistence-logic inside a transaction. This seems to make the difference and is considered best-practice anyways.
I am trying to unmarshal something like below into their JAXB equivalents, and the fields are as expected being populated with nulls
XML
<University>
<StudentFirstNames/>
</University>
JAXB POJO
public class University {
List<StudentFirstNames> studentFirstNames = null;
public void getStudentFirstNames() {
return studentFirstNames
}
public void setStudentFirstNames() {
this.studentFirsNames = studentFirstNames;
}
}
After unmarshalling I am returned back null when I lookup the member in the JAXB object
university.getStudentFirstNames() --> null
In this specific situation I am trying to resolve, I need to replace empty tags with new instance of that particular type. For example in the above, I am expecting back a new ArrayList () instead of null. I know this sounds counter intuitive but that is what I have to do for satisying code downstream.
Is there a global fix to resolve such instances. Thanks in advance.
You could initialize your field as follows to get the desired behaviour:
List<StudentFirstNames> studentFirstNames = new ArrayList<StudentFirstNames>;
To be able to differentiate between a null and empty collection you will need to use an #XmlElementWrapper annotation.
http://blog.bdoughan.com/2012/12/jaxb-representing-null-and-empty.html
This is a pretty simple request, but I just didn't find a way to do it.
I'm basically trying to set up a role in JAXB which says that whenever an null field is encountered, instead of ignoring it in the output, set it to an empty value. So for the class :
#XMLRootElement
Class Foo {
Integer num;
Date date;
….
}
When this has been marshalled into the XML file if the date field is null, my output does not have that element in it. What I want to do is include all the fields in the output; and if they are null, replace them with - say a blank. So the output should be :
<foo>
<num>123</num>
<date></date>
</foo>
Thanks,
Jalpesh.
Thanks guys for your answers.
Chris Dail - I tried your approach, and it didn't really do what I wanted. JAXB was still ignoring my null values, in spite of defining a default value for my fields.
I did stumble across the answer after somebody in the Jersey forums pointed me to documentation section 2.2.12.8 No Value.
Basically, all I had to do was to add the following to my fields :
#XmlElement(nillable = true)
Once I added that, JAXB would show up those fields when marshalling them to XML like this:
...
<num>5</num>
<date xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
....
But but but...a empty string is not a valid lexical representation for a date, so you can't do that. i.e., if you generated an XML document with an empty value for a date field, it won't validate properly.
In other words, if your date element has a minOccurs of 1 or more and not nillable, then you absolutely must have (1 or more) dates, which can't be null (or blanks, or other non-values).
As indicated in the other answer is invalid since it is not a valid date. I had a similar issue where I wanted to handle (same as ) specially. Since you cannot use null, you can use the default value mechanism in JAXB. The following will default the value if none is specified. You can through code detect this special date and handle this exception case.
#XmlElement(defaultValue="1970-01-01T00:00:00.0-00:00")
So it is possible to detected and empty date value but you just cannot use null to do it.
In MOXy u can specify how the jsonProvider must do its job for JAXB.
So when doing JAX-RS, add following code in your class derived from Application
I used this code on Tomcat 7 with good results. (eclipselink 2.4.1)
#ApplicationPath("/rest")
public class RestApplication extends Application
{
...
public Set< Object> getSingletons()
{
HashSet<Object> set = new HashSet<Object>(1);
set.add( newMoxyJsonProvider());
return set;
}
public static MOXyJsonProvider newMoxyJsonProvider()
{
MOXyJsonProvider result = new MOXyJsonProvider();
//result.setAttributePrefix("#");
result.setFormattedOutput( false);
result.setIncludeRoot( false);
result.setMarshalEmptyCollections( true);
//result.setValueWrapper("$");
return result;
}
On Glassfish 3.1.2 and WAS 8.5 however, newMoxyJsonProvider() is not needed, but then the JAXB provider gets configured by the server.
In the case of Glassfish, which comes with MOXy, i witnessed same problems with null values.
Did not check yet, but guess the answer is in configuring JAXB at application server level if possible at all.
Try this:
marshal.setListener(new MarshallerListener());
with
public class MarshallerListener extends Marshaller.Listener {
public static final String BLANK_CHAR = "";
#Override
public void beforeMarshal(Object source) {
super.beforeMarshal(source);
Field[] fields = source.getClass().getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
if (f.getType() == String.class && f.get(source) == null) {
f.set(source, BLANK_CHAR);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
I'm parsing an XML document that has nodes like the following:
<objects>
<dog>
<data1>...</data1>
<data2>...</data2>
<data3>...</data3>
</dog>
<cat>
<data1>...</data1>
<data2>...</data2>
<data3>...</data3>
</cat>
</objects>
The elements data1, data2, data3 are always consistent. Only the parent tag varies. In my object model I have a single Object which represents all of these cases. How can I get JAXB to handle this case without knowing in advance the name of the element?
#XMLAnyElement matches all the objects but doesn't create an object of the appropriate type; I get a list of Node objects instead of my object. My object currently looks something like:
public class MyObject {
protected String otherData;
#XmlAnyElement
#XmlElementWrapper(name = "objects")
protected List<MyChildObject> childObjects;
}
public class MyChildObject {
protected String data1;
protected String data2;
protected String data3;
}
Any ideas how to handle this case short of changing the incoming XML format to use <object type="dog"> elements?
If the name is truely dynamic, then I don't think JAXB will help you these. If you have a defined number of various element names then you could use inheritance like the other post suggested. If the number of elements and names is unknown I would recommend using something like this:
#XmlMixed
#XmlAnyElement
public List<Object> getObjects() {
return objects;
}
This would bring the element is a just a DOM element. You could then use JAXB a second time to go from each of the elements into your custom type.
That would be if you had to use JAXB. You might find it easier to just use the SAX or DOM APIs directly for data like this. JAXB is really intended for well defined data that can be represented as a schema.
You can use inheritance:
#XmlRootElement(name = "dog")
public class MyDogObject extends MyChildObject {
//nothing here
}
#XmlRootElement(name = "cat")
public class MyCatObject extends MyChildObject {
//nothing here
}
This way it lets you deal with the same object type, MyChildObject, yet flexibly control the XML structure. Another benefit is that should you define specific dog/cat XML nodes in the future - they can be mapped on that corresponding subclass but not the other, as expected.