johnnyB
johnnyB

Reputation: 77

JacksonXmlRootElement with dynamic localName value

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
}

Upvotes: 3

Views: 5026

Answers (2)

johnnyB
johnnyB

Reputation: 77

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 ));

Upvotes: 3

Michał Ziober
Michał Ziober

Reputation: 38625

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>

Upvotes: 1

Related Questions