Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DeserializationFeature.FAIL_ON_INVALID_SUBTYPE does not work with JsonTypeInfo.Id.CLASS #1098

Closed
szaccaria opened this issue Jan 27, 2016 · 12 comments
Milestone

Comments

@szaccaria
Copy link

I still have this problem with 2.7.0

In this #511 seem to be closed for JsonTypeInfo.Id.Name

Thanks

@cowtowncoder
Copy link
Member

I would need a unit test to show what exactly is not working, what is the problem.

@cowtowncoder
Copy link
Member

Ok, reading above-mentioned issue, is the problem that having invalid class will still result in an exception? Is this a class that does not exist (ClassLoader can not find it), or missing value?
Or something else? I would like to understand details, since handling wrt class loading is bit different from recognizing undefined type id.

@szaccaria
Copy link
Author

Hello @cowtowncoder sorry for my late replay, in this day I'm very busy.
I use the JsonTypeInfo.Id.CLASS as discriminant for unmarshal my json object in java object.

    @Path("/{id:[0-9][0-9]*}")
    @Consumes("application/json")
    public Response update(@PathParam("id") Long id, final EntityBase entityBase) {
       ....
       ....
    }

Where I annotate the "base" class as

@JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "clazz")
@MappedSuperclass
public abstract class EntityBase implements Serializable {
    ....
}

The problem is that sometimes from my client side I create a json with the property "clazz" that is not iheritance of my base class, and also I configure the object mapper like you suggest

  mapper = new ObjectMapper()
                .disable(
                        DeserializationFeature.FAIL_ON_INVALID_SUBTYPE,
                        DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
                        DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES
                ) ;

Jackson continue go to in error...
Thanks again

@cowtowncoder
Copy link
Member

@szaccaria Do you mean that value of class property is garbage; or that the name of the property used is wrong? I need to be able to create a failing test, so I need to know exact details.

@szaccaria
Copy link
Author

Hello @cowtowncoder I write something... hope that this help you, regards

Base Class

package com.popla.test;

import java.io.Serializable;

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

@JsonTypeInfo(use = com.fasterxml.jackson.annotation.JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "clazz")
public abstract class BaseClass implements Serializable{

}

FirtClass

package com.popla.test;

public class FirstClass extends BaseClass {

    private String first;

    public FirstClass() {
        super();
    }

    public FirstClass(String first) {
        super();
        this.first = first;
    }

    public String getFirst() {
        return first;
    }

    public void setFirst(String first) {
        this.first = first;
    }
}

Second Class

package com.popla.test;

public class SecondClass {

    private long second;

    public long getSecond() {
        return second;
    }

    public void setSecond(long second) {
        this.second = second;
    }

    public SecondClass(long second) {
        super();
        this.second = second;
    }

    public SecondClass() {
        super();
        // TODO Auto-generated constructor stub
    }
}

Test Class

package com.popla.test;

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

public class JackSonBug {

    ObjectMapper mapper;

    public void configureMapper() {
        mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    }

    public String createJson(String clazz) {
        StringBuffer sb = new StringBuffer();
        sb.append("{\n");
        sb.append(" \"clazz\":\"" + clazz + "\",\n");
        sb.append(" \"first\":\"TEST\"\n");
        sb.append("}\n");
        return sb.toString();
    }

    public static void main(String[] args) {

        JackSonBug jsb = new JackSonBug();

        jsb.configureMapper();

        // Test jackson's serialize, work like a charm!
        try {
            System.out.println(jsb.mapper.writeValueAsString(new FirstClass("test")));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        // Works as I expect
        try {
            String goodJson = jsb.createJson("com.popla.test.FirstClass");
            FirstClass first = (FirstClass) jsb.mapper.readValue(goodJson, BaseClass.class);
            System.out.println(first.getFirst());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // I expect a null because I set
        // DeserializationFeature.FAIL_ON_INVALID_SUBTYPE to false
        // but I get a exception
        try {
            String badJson = jsb.createJson("java.lang.String");
            FirstClass first = (FirstClass) jsb.mapper.readValue(badJson, BaseClass.class);
            System.out.println(first.getFirst());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // I expect a null because I set
        // DeserializationFeature.FAIL_ON_INVALID_SUBTYPE to false
        // but I get a exception
        try {
            String badJson = jsb.createJson("com.popla.test.SecondClass");
            FirstClass first = (FirstClass) jsb.mapper.readValue(badJson, BaseClass.class);
            System.out.println(first.getFirst());
        } catch (Exception e) {
            e.printStackTrace();
        }

        // I expect a null because I set
        // DeserializationFeature.FAIL_ON_INVALID_SUBTYPE to false
        // but I get a exception
        try {
            String badJson = jsb.createJson("com.popla.test.NotExistClass");
            FirstClass first = (FirstClass) jsb.mapper.readValue(badJson, BaseClass.class);
            System.out.println(first.getFirst());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

Ciao!

@szaccaria
Copy link
Author

Hello @cowtowncoder the code above help you?

@cowtowncoder
Copy link
Member

@szaccaria yes, I think so. Haven't had time to work on this yet, but when I do it is helpful. Thanks!

@szaccaria
Copy link
Author

Well! Thanks to you!

@Aloren
Copy link

Aloren commented May 18, 2016

Hi. I have found that issue is in TypeDeserialiserBase.
Method:

protected final JsonDeserializer<Object> _findDeserializer(DeserializationContext ctxt,
            String typeId) throws IOException
    {
        JsonDeserializer<Object> deser = _deserializers.get(typeId);
        if (deser == null) {
            /* As per [Databind#305], need to provide contextual info. But for
             * backwards compatibility, let's start by only supporting this
             * for base class, not via interface. Later on we can add this
             * to the interface, assuming deprecation at base class helps.
             */
            JavaType type = _idResolver.typeFromId(ctxt, typeId);
            if (type == null) {
                // As per [JACKSON-614], use the default impl if no type id available:
                deser = _findDefaultImplDeserializer(ctxt);
                if (deser == null) {
                    deser = _handleUnknownTypeId(ctxt, typeId, _idResolver, _baseType);
                }

assumes that _idResolver returns null in case of unknown class, but actually it throws exception and we never get to the code inside _findDefaultImplDeserializer, which checks for FAIL_ON_INVALID_SUBTYPE flag.
I would like to fix it, but I'm not sure if exception handling in case of unresolved type will be a good fix.
@cowtowncoder what do you think? Thanks

@cowtowncoder
Copy link
Member

@Aloren thank you for reporting this. Dealing with unknown class ids is bit tricky, since in some ways it can be hiding problems because unlike with type id where failure is simple (no matching mapping available), there are many reasons why lookup here might fail.

So I agree that it's a tricky situation. I assume this behavior is current one as of 2.7.4?

@Aloren
Copy link

Aloren commented May 18, 2016

Yes. It does not work for 2.7.4, but I have checked other versions till 2.2, where this flag was introduced and it does not work. I suppose, that the case we are talking about was not implemented at all. My case is following. This is json I want to decode:

{
  "some_id": 1111,
  "some_data": {
    "string": "some_string",
    "class_not_in_class_path": {
      "type": "org.company.DummyClass",
      "id": 222,
      "name": "Hello"
    }
  }
}

I read documentation for flag FAIL_ON_INVALID_SUBTYPE and supposed, that if I add:
mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false)
property class_not_in_class_path should be resolved to null. But I actually get: JsonMappingException: Invalid type id ...

cowtowncoder added a commit that referenced this issue May 25, 2016
@cowtowncoder cowtowncoder added this to the 2.7.5 milestone May 25, 2016
@szaccaria
Copy link
Author

Hello @cowtowncoder and @Aloren, i read only now... thanks thousand for your work! I'm very grateful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants