Search code examples
javajsoninheritancejacksondeserialization

Jackson using deduction-base deserialization when fields are missing


I am using Jackson to de-serialize this class

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

@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
@JsonSubTypes({
        @JsonSubTypes.Type(value = DatiSocieta.class, name = "datiSocieta"),
        @JsonSubTypes.Type(value = DatiPersonaFisica.class, name = "datiPersonaFisica") ,
        @JsonSubTypes.Type(value = DatiDittaIndividuale.class, name = "datiDittaIndividuale") }
)
public interface Dati {
}

The problem is that datiPersonaFisica and datiDittaIndividuale are ambiguous

@Data
public
class DatiPersonaFisica implements Dati {
    private String codiceFiscale;
    private String sesso;
    private String nome;
    private String cognome;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date dataNascita;
    private String luogoNascita;
    private String provinciaNascita;
    private String nazioneNascita;
}

@Data
public
class DatiDittaIndividuale implements Dati {
    private String denominazione;
    private String codiceFiscale;
    private String sesso;
    private String nome;
    private String cognome;
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date dataNascita;
    private String luogoNascita;
    private String provinciaNascita;
    private String nazioneNascita;
    private String numeroRea;
    private String provinciaCCIA;
    private String partitaIVA;
}

So it gives me the error

JSON parse error: Could not resolve subtype of [simple type, class com.XXXX.cmp.cgw.webclient.bo.Dati]: Cannot deduce unique subtype of com.xxxx.cmp.cgw.webclient.bo.Dati (2 candidates match); nested exception is com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class com.xxxx.cmp.cgw.webclient.bo.Dati]: Cannot deduce unique subtype of com.xxxx.cmp.cgw.webclient.bo.Dati (2 candidates match)\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 12, column: 5] (through reference chain: com.xxxx.cmp.cgw.webclient.dto.ClienteDTO["soggetto"]->com.xxxx.cmp.cgw.webclient.bo.Soggetto["anagrafica"])

I would like to auto-detect datiPersonaFisica if the specific fields of datiDittaIndividuale are missing. But I can't find how in the documentation.


Solution

  • The attributes in your DatiPersonaFisica class are a subset of the ones in DatiDittaIndividuale. If Jackson tries to deserialize a Json containing only the fields from DatiPersonaFisica, it has no way to tell which instance to construct, as the Json matches the fingerprint of both of your classes. See the issue #3577 on the official Jackson-databind repository.

    A Json constructed with only the fields from DatiPersonaFisica may either be an instance of DatiPersonaFisica, or an instance of DatiDittaIndividuale whose only serialized fields are the ones in common with DatiPersonaFisica; hence, the error message:

    Cannot deduce unique subtype of com.xxxx.cmp.cgw.webclient.bo.Dati (2 candidates match)

    In your case, since there is no way to tell which class is which with a DatiPersonaFisica's Json, you could either go for an implementation with JsonTypeInfo.As.PROPERTY or JsonTypeInfo.As.EXTERNAL_PROPERTY as suggested in the issues' thread.

    Solution with JsonTypeInfo.As.PROPERTY

    @JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property = "datiType"
    )
    @JsonSubTypes({
            @JsonSubTypes.Type(value = DatiSocieta.class, name = "datiSocieta"),
            @JsonSubTypes.Type(value = DatiPersonaFisica.class, name = "datiPersonaFisica"),
            @JsonSubTypes.Type(value = DatiDittaIndividuale.class, name = "datiDittaIndividuale")
    })
    public interface Dati {
    }
    
    @Data
    public class DatiSocieta implements Dati {
        // fields...
    }
    
    @Data
    public class DatiPersonaFisica implements Dati {
        // fields...
    }
    
    @Data
    public class DatiDittaIndividuale implements Dati {
        // fields...
    }
    

    Here is a demo at OneCompiler 1

    Solution with JsonTypeInfo.As.EXTERNAL_PROPERTY

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.EXTERNAL_PROPERTY,
            property = "type"
    )
    @JsonSubTypes({
            @JsonSubTypes.Type(value = DatiPersonaFisica.class, name = "DatiPersonaFisica"),
            @JsonSubTypes.Type(value = DatiDittaIndividuale.class, name = "DatiDittaIndividuale")
    })
    public class Company {
        protected Dati dati;
        
        //constructors
        
        //getters and setters
    }
    
    public class Dati {
        private String nome;
        private String cognome;
    
        //constructors
    
        //getters and setters
    }
    
    public class DatiPersonaFisica extends Company {
    
        //constructors
    
        //getters and setters
    }
    
    public class DatiDittaIndividuale extends Company {
        private String denominazione;
    
        //constructors
    
        //getters and setters
    }
    

    Here is a link for the second implementation at OneCompiler 2