Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 728
Edu Garcia Avatar asked Nov 13 '22 21:11

Edu Garcia


1 Answers

@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.

like image 199
EdC Avatar answered Dec 16 '22 13:12

EdC