Search code examples
javajsonjackson

The issue is with mapping objects to the model during deserialization


colleagues! I've encountered an issue with object mapping during deserialization. We have an interface Source, which is implemented by two classes (SimpleSource and ComplexSource). Instances of these classes are contained in lists within a separate object called SourceContainer. The JSON stores the configuration of SourceContainer, which we want to deserialize. The error is as follows:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class org.example.SimpleSource]: missing type id property 'jacksonType' (for POJO property 'simpleSources')
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 5, column: 5] (through reference chain: org.example.SourceContainer["simpleSources"]->java.util.ArrayList[0])

To reproduce the error, I'll provide a concise list of all the classes in my program

package org.example;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) {
        InputStream jsonFile = Main.class.getClassLoader().getResourceAsStream("sourceContainer.json");
        
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        try {
            SourceContainer sourceContainer = objectMapper.readValue(jsonFile, SourceContainer.class);
            sourceContainer.printAllSources();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package org.example;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "jacksonType"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = SimpleSource.class, name = "simpleSource"),
        @JsonSubTypes.Type(value = ComplexSource.class, name = "complexSource")
})
public interface Source {
    String getDescription();
}
package org.example;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class SimpleSource implements Source {

    private String description;

    public SimpleSource(String description) {
        this.description = description;
    }

    @Override
    public String getDescription() {
        return "Simple Source: " + description;
    }
}
package org.example;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class ComplexSource implements Source {

    private String description;
    private String details;

    public ComplexSource(String description, String details) {
        this.description = description;
        this.details = details;
    }

    @Override
    public String getDescription() {
        return "Complex Source: " + description + " - " + details;
    }
}
package org.example;

import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
public class SourceContainer {

    private List<SimpleSource> simpleSources;
    private List<ComplexSource> complexSources;

    public SourceContainer() {
        this.simpleSources = new ArrayList<>();
        this.complexSources = new ArrayList<>();
    }

    // Добавление SimpleSource
    public void addSimpleSource(SimpleSource source) {
        simpleSources.add(source);
    }

    // Добавление ComplexSource
    public void addComplexSource(ComplexSource source) {
        complexSources.add(source);
    }

    // Печать всех источников
    public void printAllSources() {
        System.out.println("Simple Sources:");
        for (SimpleSource source : simpleSources) {
            System.out.println(source.getDescription());
        }

        System.out.println("Complex Sources:");
        for (ComplexSource source : complexSources) {
            System.out.println(source.getDescription());
        }
    }
}

The issue is with mapping objects to the model during deserialization


Solution

  • Jackson is failing to deserialize your JSON because the data structure described in the file sourceContainer.json does not exhibit the property jacksonType.

    Could not resolve subtype of [simple type, class org.example.SimpleSource]: missing type id property 'jacksonType' (for POJO property 'simpleSources')

    The property jacksonType is necessary because it's used by the annotation @JsonSubTypes to determine the correct subtype of Source and to properly deserialize your JSON.