Inheritance::Cannot deduce unique subtype (multiple candidates)

3,534 views
Skip to first unread message

Karsil

unread,
Feb 8, 2022, 12:30:59 PM2/8/22
to jackson-user
Hey everyone,

I'm trying to solve an inheritance problem where a clear deduction is currently not possible. A toy example:

// BASECLASS
@JsonSubTypes( {
@JsonSubTypes.Type( Child.class )
} )
@JsonTypeInfo( use = JsonTypeInfo.Id.DEDUCTION )
public class BaseModel
{
@JsonProperty( value = "baseOne", required = true )
@JsonPropertyDescription( "some value of base class" )
protected String m_valueOne = "defaultValue";

@JsonProperty( value = "basTwo", required = true )
@JsonPropertyDescription( "another value of base class" )
protected String m_valueTwo;

public BaseModel()
{
}

public BaseModel( final String p_value )
{
 m_valueOne = p_value;
}
}

// CHILD CLASS
@JsonInclude( JsonInclude.Include.NON_NULL )
public class Child extends BaseModel
{
@JsonProperty( value = "childValueFirst", required = true )
@JsonPropertyDescription( "some value of child class" )
private String m_child;

public Child()
{
}

public Child( final String p_value )
{
m_child = p_value;
}
}

// TEST
public class TestDemo
{
@Test
public void testDefault() throws JsonProcessingException
{
final List<BaseModel> l_list = Stream.of( new BaseModel(), new Child() ).collect( Collectors.toList() );

final ObjectMapper l_mapper = new ObjectMapper();
final String l_json = l_mapper.writeValueAsString( l_list );
System.out.println( l_json );
final List<BaseModel> l_out = l_mapper.readValue( l_json, new TypeReference<List<BaseModel>>()
{ } );
}
}

Error message (during deserialization):
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not resolve subtype of [simple type, class testproject.demo.BaseModel]: Cannot deduce unique subtype of `testproject.demo.BaseModel` (2 candidates match)

The error is quite logical: For the base class, two possibilities can actually be deserialized.

I already have encountered the defaultImpl property:
@JsonTypeInfo( ..., defaultImpl = BaseClass.class )
But this leads to a high-priority, default behaviour (all child objects are deserialized with this default class, which is not intended). I'd rather like to give it the lowest priority.

But how actually to solve this? Thanks in advance for any help!

Tatu Saloranta

unread,
Feb 9, 2022, 12:44:17 PM2/9/22
to jackso...@googlegroups.com
I don't think this is possible at this point in time -- deduction
requires a unique set of properties.

You could file an issue asking for an improvement here, outlining
details of how you think things should work
wrt. logic to use. There are a few issues like:

https://github.com/FasterXML/jackson-databind/issues/3289

related to this feature -- due to all limitations I am beginning to
wonder if this feature should not have been added
at all in its current form :-(
You may want to add a comment there.

Basically everyone seems to have bit different ideas of logic to use,
and it is difficult to have centralized unified
logic to deal with this. As opposed to users maybe registering a
handler that operated on JsonNode of the full
content, or something (which is difficult to handle via
`@JsonTypeInfo`, processing model).

Still, mr @drekbour might have some good ideas wrt this particular aspect.

-+ Tatu +-

Karsil

unread,
Feb 10, 2022, 6:08:47 AM2/10/22
to jackson-user
For now, we solved it like this:

// BASE CLASS (now abstract)
@JsonSubTypes( {
@JsonSubTypes.Type( value = Child.class, name = Child.NAME )
} )
@JsonTypeInfo( use = JsonTypeInfo.Id.DEDUCTION )
public abstract class BaseModel
{
// the rest as above
}

// CHILD
@JsonInclude( JsonInclude.Include.NON_NULL )
public class Child extends BaseModel
{
@JsonIgnore
public static final transient String NAME = "child";

// the rest as above
}

This can then be repeated for multiple childs, but you will have to pass a key with the value of the child, above "child".

Tatu Saloranta

unread,
Feb 10, 2022, 4:29:57 PM2/10/22
to jackson-user
On Thu, Feb 10, 2022 at 3:08 AM 'Karsil' via jackson-user <jackso...@googlegroups.com> wrote:
For now, we solved it like this:

// BASE CLASS (now abstract)
@JsonSubTypes( {
@JsonSubTypes.Type( value = Child.class, name = Child.NAME )
} )
@JsonTypeInfo( use = JsonTypeInfo.Id.DEDUCTION )
public abstract class BaseModel
{
// the rest as above
}

// CHILD
@JsonInclude( JsonInclude.Include.NON_NULL )
public class Child extends BaseModel
{
@JsonIgnore
public static final transient String NAME = "child";

// the rest as above
}

This can then be repeated for multiple childs, but you will have to pass a key with the value of the child, above "child".

Not sure I understand... making base class "abstract" sort of makes sense (for it to be skipped).
But what do you mean by passing a "key"?
If you mean adding a new type property, wouldn't it make more sense to not use DEDUCTION at all
but the standard, type id property based approach?

-+ Tatu +-

 
--
You received this message because you are subscribed to the Google Groups "jackson-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jackson-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jackson-user/77db987c-4f47-4900-8140-886564a6133en%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages