Home > Software design >  How to serialize/deserialize (json) a class that has an attribute that's an interface
How to serialize/deserialize (json) a class that has an attribute that's an interface

Time:04-08

I'd like to serialize/deserialize (json) a class that contains an attribute that is an interface, but the underlying class doesn't have any attributes. The below is my most simplified case and my best attempt at what to do.

This throws an error when trying to deserialize No suitable constructor found for type [simple type, class com.example.Bar]: can not instantiate from JSON object (need to add/enable type information?) at [Source: java.io.StringReader@301ec38b; line: 1, column: 2]

public interface FooInterface {
    String doThing();
}

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
@EqualsAndHashCode
public class Foo implements FooInterface {
    @Override
    public String doThing() {
        return "foo";
    }
}

@Getter
@Setter
@EqualsAndHashCode
public class Bar {
    FooInterface foo;

    public Bar(FooInterface foo) {
        this.foo = foo;
    }
}
    @Test
    public void fooTest() throws IOException {
        Foo foo = new Foo();
        Bar bar = new Bar(foo);

        String serialized = new ObjectMapper().writeValueAsString(bar); // = {"foo":{}}
        Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
        Assert.assertEquals(bar, deserialized);
    }

CodePudding user response:

Please add default constructor to class Bar and I guess your issue should be resolved.

@Getter
@Setter
@EqualsAndHashCode
public class Bar {
    FooInterface foo;
    
    public Bar() {}

    public Bar(FooInterface foo) {
        this.foo = foo;
    }
}

Do let me know if this doesn't solve your problem, I will try to dig deeper.

CodePudding user response:

As @Aditya mentioned I was missing the default constructor which was causing the error I was having, but then the new error led me to finding this question which was the crux of the problem that this question was asking about.

Looks like I misunderstood what the JsonAutoDetect annotation did. Below is the code that ended up working for me.

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "type")
@JsonSubTypes({
        @JsonSubTypes.Type(value = Foo.class),
})
public interface FooInterface {
    String doThing();
}

@EqualsAndHashCode
public class Foo implements FooInterface {
    @Override
    public String doThing() {
        return "foo";
    }
}

@Getter
@Setter
@EqualsAndHashCode
public class Bar {
    FooInterface foo;

    public Bar() {}

    public Bar(FooInterface foo) {
        this.foo = foo;
    }
}

    @Test
    public void fooTest() throws IOException {
        Foo foo = new Foo();
        Bar bar = new Bar(foo);

        String serialized = new ObjectMapper().writeValueAsString(bar); // {"foo":{"type":"Foo"}}
        Bar deserialized = new ObjectMapper().readValue(serialized, Bar.class);
        Assert.assertEquals(bar, deserialized);
    }
  • Related