Ghoughpteighbteau
Ghoughpteighbteau

Reputation: 86

Providing Jackson Mapper multiple ways to deserialize the same object

I'm trying to deserialize two types of json:

{
    name: "bob",
    worksAt: {
        name: "Bobs department store",
        location: "downtown"
    },
    age: 46
}

and

{
    name: "Tom",
    worksAt: "company:Bobs department store",
    age: 27
}

into these objects:

UML of classes

The first way creates two new objects, the second way requests the object from the database based on the contents of a string.

sort of like how jackson mapper can deserialize an arbitrary string into an object, for objects like this:

public class Company{
    public String name;
    public Employee[] employees
    public Company(){}
    public Company(String json){
        //turn string into object using whatever encoding you want blah blah blah...
    }
}

The trouble is I need both. I need it to handle objects and strings. Both could arrive from the same input.

The first think I tried was making a Converter

It says these create a delegate type to pass to the deserializer, but the converter is always applied even when the datatype isn't a string. So that didn't work.

I've also tried a normal deserializer, but I can't find a way to defer to the BeanDeserializer. The beanDeserializer is so complicated that I can't manually instantiate it. I also see no way to defer to a default deserializer in jackson mapper.

Do I have to re-implement jackson mappers deserialization to do this? Is there any way for a deserializer to say "I can't do this, use the default implementation."?

Edit: Some further progress. Based on the Jackson Mapper source code, it looks like you can instatiate bean deserializers like this:

DeserializationConfig config = ctxt.getConfig();
JavaType type = config.constructType(_valueClass);
BeanDescription introspect = config.introspect(type);
JsonDeserializer<Object> beanDeserializer = ctxt.getFactory().createBeanDeserializer(ctxt, type , introspect);

but for some reason all the _beanProperties have the FailingDeserializer set for their _valueDeserializer and the whole thing fails. So I have no idea why that happens...

Upvotes: 3

Views: 5805

Answers (3)

Ann Kilzer
Ann Kilzer

Reputation: 1361

Have you tried writing a custom deserializer? This gives you the most control on how Jackson deserializes the object. You may be able to try to deserialize one way, and if there's an error, try another way.

Jackson can also handle polymorphic deserialization, though this would require a small change to the json to include type information, and it sounds like your problem constraints might not allow that.

Upvotes: 2

Sergio
Sergio

Reputation: 1179

With recent versions of Jackson (2.8+ I think, definitely works with 2.9) you can use multiple @JsonCreator and do something like this:

public class Company {
    private String name;
    private String location;

    private Company(String name, String location) {
        this.name = name;
        this.location = location;
    }
    private Company(String stringRepresentation) {
       // add code here to parse string and extract name and location
    }

    @JsonCreator
    private static Company fromJson(
            @JsonProperty("name") String name,
            @JsonProperty("location") String location)
    {
        return new Company(name, location);
    }

    @JsonCreator
    private static Company fromJson(String str) {
        return Company(str);
    }
}

Upvotes: 2

Jim E-H
Jim E-H

Reputation: 343

If I understand the problem correctly, I would recommend using JsonNode. You can define a setter in your top-level type like this:

setWorksAt(JsonNode node) {
    if (node.getNodeType == JsonNodeType.STRING) {
        String name = node.getText();
        name = name.substring(name.lastIndexOf(':'));
        this.company = new Company(name);
    } else if (node.getNodeType == JsonNodeType.OBJECT) {
        this.company = mapper.treeToValue(node, Company.class);
    }
}

That allows you to handle the two separate worksFor inputs, while still allowing the standard mapper to handle any substructures for the OBJECT case.

Upvotes: 1

Related Questions