3

I am updating a POJO that we map to XML and the only difference is that someone wants both the old XML and some new, and with the only difference being the root Wrapper name (all the same fields), e.g. currently the root Xml Tag is set to ExistingName and they want a new value like BrandNewName with all the same fields. And still get the old. Is there a way to just flip this in the POJO?

I figured I could do it with some inheritance and a base class with two implementations, but seemed overkill

I know I can set the root tag with @JacksonXmlRootElement, but is it possible to set it to a variable name.

@JacksonXmlRootElement(localName = 'ExistingName')
class MyPojo {
    String commonVar1
    String commonVar1
    String commonVar1
}
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146
johnnyB
  • 77
  • 1
  • 10
  • Root element name in `XML` during deserialisation should not be a problem. `...` - will be deserialised and `...` will be properly deserialised. What problem do you have with old `XML` files? – Michał Ziober Oct 15 '19 at 15:40
  • There is no problem with the old xml. It is valid and they still want it. They just also want in other situations the same xml, with a different root name. So, are you saying 1) Deserialize with it is as 2) Replace `ExistingName` w/ `BrandNewName`? – johnnyB Oct 16 '19 at 19:22
  • So, you want to serialise the same `POJO` as two different root elements, am I right? Do you use one global `ObjectMapper` configuration or you use many local instances? – Michał Ziober Oct 16 '19 at 20:10
  • ``` XmlMapper xmlMapper = new XmlMapper() String xml = xmlMapper.writeValueAsString(save) ``` Note, I was able to do it by making a parent class with all the fields and extending to two child classes, that simply have different `@JacksonXmlRootElement` – johnnyB Oct 17 '19 at 13:40

2 Answers2

3

I ended up finding Get Jackson XMLMapper to set root element name in code and just setting using

XmlMapper mapper = new XmlMapper();
.writer()
.withRootName(myFieldName)
.writeValueAsString(myPojo ));
johnnyB
  • 77
  • 1
  • 10
1

You can use MixIn feature and dynamically declare name for root type:

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        XmlMapper xmlMapperOld = new XmlMapper();
        xmlMapperOld.addMixIn(MyPojo.class, MyPojoExistingNameMinIn.class);

        XmlMapper xmlMapperNew = new XmlMapper();
        xmlMapperNew.addMixIn(MyPojo.class, MyPojoBranNewNameMinIn.class);

        System.out.println(xmlMapperOld.writeValueAsString(new MyPojo()));
        System.out.println(xmlMapperNew.writeValueAsString(new MyPojo()));
    }
}

@JacksonXmlRootElement(localName = "ExistingName")
interface MyPojoExistingNameMinIn {
}

@JacksonXmlRootElement(localName = "BrandNewName")
interface MyPojoBranNewNameMinIn {
}

Above code prints:

<ExistingName><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName>
<BrandNewName><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></BrandNewName>

But we can also create an extra POJO to store object and name we want to assign during serialisation process. To make it work we need to implement custom JsonSerializer and implement that behaviour. Example solution could look like below:

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.util.NameTransformer;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer;

import javax.xml.namespace.QName;
import java.io.IOException;
import java.util.Objects;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        XmlMapper xmlMapper = new XmlMapper();
        for (char c = 65; c < 70; c++) {
            System.out.println(xmlMapper.writeValueAsString(new PersistAs("ExistingName_" + c, new MyPojo())));
        }
    }
}

class PersistAsJsonSerializer extends JsonSerializer<PersistAs> {

    @Override
    public void serialize(PersistAs value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        ToXmlGenerator xmlGen = (ToXmlGenerator) gen;
        // set desired name
        xmlGen.setNextName(new QName(value.getName()));
        xmlGen.writeStartObject();

        // serialise fields
        XmlBeanSerializer serializer = (XmlBeanSerializer) serializers.findValueSerializer(value.getValue().getClass());
        JsonSerializer<Object> unwrappingSerializer = serializer.unwrappingSerializer(NameTransformer.NOP);
        unwrappingSerializer.serialize(value.getValue(), gen, serializers);

        // end of root
        gen.writeEndObject();
    }
}

@JsonSerialize(using = PersistAsJsonSerializer.class)
class PersistAs {
    private final String name;
    private final Object value;

    public PersistAs(String name, Object value) {
        this.name = Objects.requireNonNull(name);
        this.value = Objects.requireNonNull(value);
    }

    public String getName() {
        return name;
    }

    public Object getValue() {
        return value;
    }

    @Override
    public String toString() {
        return "PersistAs{" +
                "name='" + name + '\'' +
                ", value=" + value +
                '}';
    }
}

Above code prints:

<ExistingName_A><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName_A>
<ExistingName_B><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName_B>
<ExistingName_C><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName_C>
<ExistingName_D><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName_D>
<ExistingName_E><commonVar1>Var1</commonVar1><commonVar2>Var2</commonVar2><commonVar3>Var3</commonVar3></ExistingName_E>
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146