Changing class without changing a schema - java

I have a xsd schema which I'm not allowed to change. It produce generated java classes.
Assume that classes looks as follows:
class Data {
protected List<Value> value;
...
}
class Value {
...
}
Now I need to have my own MyValue ideally to be extended from Value.
class MyValue extends Value {
Integer myOwnField1;
Long anotherProperty;
}
And be able to say unmarshaller to use MyValue instead of Value when it parses xml file.
Later I would be able to use the fact that MyValue can contain some useful new fields inside, make operations over them, change them, etc. So I want extend the functionality which I have in schema without changing it.
Is that somehow possible to replace Value by MyValue for unmarshaller?
Apart from the obvious way to create a Map where I can map object which was generated by unmarshaller with my own fields and properties in MyValue. I'd like to avoid this way.

Can you load the list using bean properties?
public void setValue(List<Value> value) {
this.value = ...convert to List<MyValue>...
}

You might be interested in unmarshalling by declared type: indeed, if you override your XSD from the root element to every child element you need to override, you could use a second argument in unmarshal method to sepecify your custom mapping.
<T> JAXBElement<T> unmarshal(Source source,
Class<T> declaredType)
throws JAXBException
[...] declaredType - appropriate JAXB mapped class to hold
source's xml root element
(see Unmarshaller javadoc for more detailed info)

Related

Is there a custom Json Serializer pattern to serialize a property differently including the property's key name?

I have a class that needs to be serialized
public class Abc
{
private long age;
private JaxBElement<Foo> fooWrapper;
// other properties
}
The expected output JSON is
{
"age": 24,
"my_own_key": "my_own_value" // the key should not be "fooWrapper"
A constraint is that the original class Abc cannot be modified since it is generated out of xjc and I don't want to explore custom class using bindings yet.
I have tried custom serializers, bean modifiers etc. for the JaxBElement and all of them allow me to control the serialization. But they work at the VALUE of the property only. They don't allow me to change stuff at the "KEY-VALUE" level. This is the crux of the question. The key is already written out for the property before the custom serializer is invoked to control the value.
E.g. My custom serializer is invoked only after the Jackson system has emitted out the key
"fooWrapper": // now for the value part, let me invoke the custom serializer
So the output JSON always contains the "fooWrapper" key.
{ "fooWrapper": { "any-key": "any-value" } }
// the fooWrapper is already emitted out. That is what needs to be controlled.
My ask is to control the serialization at a higher level, such that both the key and value can be controlled. So when class Abc is being serialized, the fooWrapper property should not be written as a key at all and some custom serializer should be invoked.
Another constraint is that there are several classes like Abc which may have such JaxBElement. It is not known ahead of time. So there needs to be a generic way to attach the custom serializer.
The pseudo ask is really that we be able to attach a custom serializer to any class which has a property that matches a pattern such that the serializer can control the name of the property (or the whole key-value blob) written out.
Also, the problem is not specific to JaxBElement per se. It could be any property. The problem is more about controlled serialization INCLUDING the key being written out.
Maybe you just use the incorrect kind of Serializer. This post, although a bit old should show you how to do what you want with StdSerializer.
This kind of serializer allows you to control both the key and the value.
If you want to control serialisation of key-value pair you need to register custom serialiser not only for JaxBElement<Foo> fooWrapper but also for Abc class to change a key value.
Since it is not a generic solution you can also try to create MixIn class or interface and provide extra configuration:
interface MixInA {
#JsonSerialize(using = JAXBElementJsonSerializer.class)
#JsonProperty("newProperty")
JAXBElement<Foo> getFooWrapper();
}
See also:
What is equivalent code settings for #JSonIgnore annotation?
Make Jackson serializer override specific ignored fields
Downside of this solution is you have to find all types for which you have to register MixIn class or interface. In case fields are different you need to create many different getters or many different MixIn interfaces to cover them all.
So, probably them most flexible solution would be to implement custom com.fasterxml.jackson.databind.AnnotationIntrospector and for given type you can return custom serialiser and custom name. Simple example:
class DynamicJaxbAnnotationIntrospector extends AnnotationIntrospector {
#Override
public Version version() {
return new Version(1, 0, 0, "Dynamic JaxbElement", "your.package", "jackson.dynamic.jaxb");
}
#Override
public Object findSerializer(Annotated am) {
if (am.getRawType() == JAXBElement.class) {
return new JAXBElementJsonSerializer();
}
return super.findSerializer(am);
}
#Override
public PropertyName findNameForSerialization(Annotated a) {
if (a.getRawType() == JAXBElement.class) {
return new PropertyName("newProperty");
}
return super.findNameForSerialization(a);
}
}
See also below article how to use it:
How to serialise Enums as both Object Shape and default string?

Implicit default values when deserializing JSON using Jackson

When deserializing a variety of JSON messages, I want to provide a default value for attributes of a certain type. It is generally suggested to simply specify the value in the Class, but this is error-prone if you have to do this across many Classes. You might forget one and end up with null instead of a default value. My intention is to set every property that is an Optional<T> to Optional.absent. Since null is exactly what Optional is trying to eliminate, using them with Jackson has proven to be frustrating.
Most features of Jackson that allow you to customize the deserialization process focus on the JSON that is the input, not around the process of instantiating the Object that you are deserializing into. The closest I seem to be getting to a general solution is by building my own ValueInstantiator, but there are two remaining issues I have:
how do I make it only instantiate Optional as absent but not interfere with the rest of the instantiation process?
how do I wire the end result into my ObjectMapper?
UPDATE: I want to clarify that I am looking for a solution that does not involve modifying each Class that contains Optional's. I'm opposed to violating the DRY principle. Me or my colleagues should not have to think about having to do something extra every time we add Optional's to a new or existing Class. I want to be able to say, "make every Optional field in every Class I deserialize into, pre-filled with Absent", only once, and be done with it.
That means the following are out:
abstract parent class (need to declare)
custom Builder/Creator/JsonDeserializer (needs annotation on each applicable class)
MixIn's? I tried this, combined with reflection, but I don't know how to access the Class I'm being mixed into...
Specifically for java.lang.Optional, there is a module by the Jackson guys themselves: https://github.com/FasterXML/jackson-datatype-jdk8
Guava Optional is covered by https://github.com/FasterXML/jackson-datatype-guava
It will create a Optional.absent for null's, but not for absent JSON values :-(.
See https://github.com/FasterXML/jackson-databind/issues/618 and https://github.com/FasterXML/jackson-datatype-jdk8/issues/2.
So you're stuck with initializing your Optionals just as you should initialize collections. It is a good practice, so you should be able to enforce it.
private Optional<Xxx> xxx = Optional.absent();
private List<Yyy> yyys = Lists.newArrayList();
You can write a custom deserializer to handle the default value. Effectively you will extend the appropriate deserializer for the type of object you are deserializing, get the deserialized value, and if it's null just return the appropriate default value.
Here's a quick way to do it with Strings:
public class DefaultStringModule extends SimpleModule {
private static final String NAME = "DefaultStringModule";
private static final String DEFAULT_VALUE = "[DEFAULT]";
public DefaultStringModule() {
super(NAME, ModuleVersion.instance.version());
addDeserializer(String.class, new DefaultStringDeserializer());
}
private static class DefaultStringDeserializer extends StdScalarDeserializer<String> {
public DefaultStringDeserializer() {
super(String.class);
}
public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
String deserialized = jsonParser.getValueAsString();
// Use a default value instead of null
return deserialized == null ? DEFAULT_VALUE : deserialized;
}
#Override
public Object deserializeWithType(JsonParser jp, DeserializationContext ctxt, TypeDeserializer typeDeserializer) throws IOException {
return deserialize(jp, ctxt);
}
}
}
To use this with an ObjectMapper you can register the module on the instance:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new DefaultStringModule());
To handle default values for fields not present in the JSON, I've typically seen this done through the use of a builder class that will construct the class using the values supplied and add any default values for the missing fields. Then, on the deserialized class (e.g. MyClass), add a #JsonDeserialize(builder = MyClass.Builder.class) annotation to indicate to Jackson to deserialize MyClass by way of the builder class.
Your value object should initialize these values as absent. That's the way to ensure that default values have no nulls. Guava module's Optional handler really should only deserialize them as "absent" (even with explicit JSON nulls), and never as nulls, with later versions.
But since Jackson only operates on JSON properties that exist (and not on things that could exist but do not), POJO still needs to have default absent assignment.

How to use JAXB to store an Array Class in a Class<?> field

I would like to marshall/unmarshall a Java Object with a class<?> datatype field. This works well if the Class in known by the JAXB Context but it doesn't work to store a Java Array Class.
My Object :
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class MyClass {
private Object value;
private Class<?> datatype;
}
If I do: c.setDatatype(Short.class); it works.
If I do: c.setDatatype(Short[][].class);it stores <datatype>[[Ljava.lang.Short;</datatype> in the XML file, but the Datatype field is null when unmarshalled.
I added Short[][].class to the JAXBContext. This works if I use XStream.
I think I can make this work with a #XmlJavaTypeAdapter to handle the array case. But is there a better way?
Thank you.

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.

How to stream large Files using JAXB Marshaller?

The Problem I'm facing is how to marshall a large list of objects into a single XML File, so large I can not marshall the complete list in one step. I have a method that returns these objects in chunks, but then I marshall these using JAXB, the marshaller returns with an exception that these objects are no root elements. This is ok for the normal case there you want to marshall the complete document in one step, but it also happens if I set the JAXB_FRAGMENT Property to true.
This is the desired XML output:
<rootElem>
<startDescription></startDescription>
<repeatingElem></repeatingElem>
<repeatingElem></repeatingElem>...
</rootElem>
So I assume I need some kind of listener that dynamically loads the next chunk of repeatingElements to feed it to the marshaller before he would write the closing tag of the rootElement. But how to do that? Up until now I only used JAXB to marshall small files and the JAXB documentation does not give much hints for that use case.
I'm aware that this is an old question but I came across it while searching for duplicates of another similar question.
As #skaffman suggests, you want to Marshal with JAXB_FRAGMENT enabled and your objects wrapped in JAXBElement. You then repeatedly marshal each individual instance of the repeated element. Basically it sounds like you want something roughly like this:
public class StreamingMarshal<T>
{
private XMLStreamWriter xmlOut;
private Marshaller marshaller;
private final Class<T> type;
public StreamingMarshal(Class<T> type) throws JAXBException
{
this.type = type;
JAXBContext context = JAXBContext.newInstance(type);
this.marshaller = context.createMarshaller();
this.marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
}
public void open(String filename) throws XMLStreamException, IOException
{
xmlOut = XMLOutputFactory.newFactory().createXMLStreamWriter(new FileOutputStream(filename));
xmlOut.writeStartDocument();
xmlOut.writeStartElement("rootElement");
}
public void write(T t) throws JAXBException
{
JAXBElement<T> element = new JAXBElement<T>(QName.valueOf(type.getSimpleName()), type, t);
marshaller.marshal(element, xmlOut);
}
public void close() throws XMLStreamException
{
xmlOut.writeEndDocument();
xmlOut.close();
}
}
As you've discovered, if a class does not have the #XmlRootElement annotation, then you can't pass an instance of that class to the marshaller. However, there is an easy way around this - wrap the object in a JAXBElement, and pass that to the marshaller instead.
Now JAXBElement is a rather clumsy beast, but what it does is contains the element name and namespace of the object that you want to marshal, information which would normally be contained in the #XmlRootElement annotation. As long as you have the name and namespace, you can construct a JAXBElement to wrap your POJO, and marshal that.
If your POJOs were generated by XJC, then it will also have generated an ObjectFactory class which contains factory methods for building JAXBElement wrappers for you, making things a bit easier.
You'll still have to use the JAXB_FRAGMENT property for the repeating inner elements, otherwise JAXB will generate stuff like the XML prolog each time, which you don't want.
I don't know much of JAXB, so I can't help. But if you don't mind, I have a suggestion.
Writing XML is a lot easier than reading it, so an solution for your problem might be to use a more "low level" approach. Just write your own marshaller using one of the available open source libraries for XML. I think you can easily do what you want using dom4j.

Categories