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

default typing fails on 2.7.0 - 2.8.2-SNAPSHOT #94

Open
NagyGa1 opened this issue Jul 24, 2016 · 4 comments
Open

default typing fails on 2.7.0 - 2.8.2-SNAPSHOT #94

NagyGa1 opened this issue Jul 24, 2016 · 4 comments

Comments

@NagyGa1
Copy link
Contributor

NagyGa1 commented Jul 24, 2016

Below is the same as ForceLazyLoadingTest, with added enableDefaultTyping().

public class DefaultTypingFailsTest extends BaseTest
{
    @Test
    public void testDefaultTypingFails() throws Exception
    {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistenceUnit");

        try {
            EntityManager em = emf.createEntityManager();

            // false -> no forcing of lazy loading
            ObjectMapper mapper = mapperWithModule(true).enableDefaultTyping();

            Customer customer = em.find(Customer.class, 103);
            assertFalse(Hibernate.isInitialized(customer.getPayments()));
            String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(customer);
            // should force loading...
            Set<Payment> payments = customer.getPayments();
            /*
            System.out.println("--- JSON ---");
            System.out.println(json);
            System.out.println("--- /JSON ---");
            */

            assertTrue(Hibernate.isInitialized(payments));
            // TODO: verify
            assertNotNull(json);

            Map<?,?> stuff = mapper.readValue(json, Map.class);

            assertTrue(stuff.containsKey("payments"));
            assertTrue(stuff.containsKey("orders"));
            assertNull(stuff.get("orderes"));

        } finally {
            emf.close();
        }
    }
}

Result during Map<?,?> stuff = mapper.readValue(json, Map.class); (and it is not because of the PersistentCollections, as I am working on replacing them and the result is the same with java.util.* collections):

com.fasterxml.jackson.databind.JsonMappingException: Unexpected token (START_OBJECT),
expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for
class java.util.Map
 at [Source: {
  "customerNumber" : 103,
  "customerName" : "Atelier graphique",
  "contactLastName" : "Schmitt",
  "contactFirstName" : "Carine ",
  "phone" : "40.32.2555",
  "addressLine1" : "54, rue Royale",
  "addressLine2" : null,
  "city" : "Nantes",
  "state" : null,
  "postalCode" : "44000",
  "country" : "France",
  "creditLimit" : 21000.0,
  "payments" : [ "org.hibernate.collection.internal.PersistentSet", [ {
    "@type" : "Payment",
    "id" : {
      "customerNumber" : 103,
      "checkNumber" : "HQ336336"
    },
    "paymentDate" : 1098115200000,
    "amount" : 5307.98
  }, {
    "@type" : "Payment",
    "id" : {
      "customerNumber" : 103,
      "checkNumber" : "OM314933"
    },
    "paymentDate" : 1103299200000,
    "amount" : 2311.68
  }, {
    "@type" : "Payment",
    "id" : {
      "customerNumber" : 103,
      "checkNumber" : "JM555205"
    },
    "paymentDate" : 1054742400000,
    "amount" : 16560.3
  } ] ],
  "orders" : [ "org.hibernate.collection.internal.PersistentSet", [ [ "com.fasterxml.jackson.datatype.hibernate4.data.Order", {
    "orderNumber" : 10298,
    "orderDate" : 1096214400000,
    "requiredDate" : 1096905600000,
    "shippedDate" : 1096560000000,
    "status" : "Shipped",
    "comments" : null,
    "orderDetails" : [ "org.hibernate.collection.internal.PersistentSet", [ [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10298,
        "productCode" : "S18_2625"
      },
      "quantityOrdered" : 32,
      "priceEach" : 48.46,
      "orderLineNumber" : 2
    } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10298,
        "productCode" : "S10_2016"
      },
      "quantityOrdered" : 39,
      "priceEach" : 96.34,
      "orderLineNumber" : 1
    } ] ] ]
  } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.Order", {
    "orderNumber" : 10345,
    "orderDate" : 1101312000000,
    "requiredDate" : 1101830400000,
    "shippedDate" : 1101398400000,
    "status" : "Shipped",
    "comments" : null,
    "orderDetails" : [ "org.hibernate.collection.internal.PersistentSet", [ [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10345,
        "productCode" : "S24_2022"
      },
      "quantityOrdered" : 43,
      "priceEach" : 53.76,
      "orderLineNumber" : 1
    } ] ] ]
  } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.Order", {
    "orderNumber" : 10123,
    "orderDate" : 1053360000000,
    "requiredDate" : 1054137600000,
    "shippedDate" : 1053532800000,
    "status" : "Shipped",
    "comments" : null,
    "orderDetails" : [ "org.hibernate.collection.internal.PersistentSet", [ [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10123,
        "productCode" : "S18_3685"
      },
      "quantityOrdered" : 34,
      "priceEach" : 156.82,
      "orderLineNumber" : 4
    } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10123,
        "productCode" : "S24_1628"
      },
      "quantityOrdered" : 50,
      "priceEach" : 59.87,
      "orderLineNumber" : 1
    } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10123,
        "productCode" : "S18_1589"
      },
      "quantityOrdered" : 26,
      "priceEach" : 118.22,
      "orderLineNumber" : 2
    } ], [ "com.fasterxml.jackson.datatype.hibernate4.data.OrderDetail", {
      "id" : {
        "orderNumber" : 10123,
        "productCode" : "S18_2870"
      },
      "quantityOrdered" : 46,
      "priceEach" : 112.2,
      "orderLineNumber" : 3
    } ] ] ]
  } ] ] ]
}; line: 1, column: 1]

    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261)
    at com.fasterxml.jackson.databind.DeserializationContext.wrongTokenException(DeserializationContext.java:1340)
    at com.fasterxml.jackson.databind.DeserializationContext.reportWrongTokenException(DeserializationContext.java:1196)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._locateTypeId(AsArrayTypeDeserializer.java:137)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer._deserialize(AsArrayTypeDeserializer.java:96)
    at com.fasterxml.jackson.databind.jsontype.impl.AsArrayTypeDeserializer.deserializeTypedFromObject(AsArrayTypeDeserializer.java:61)
    at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserializeWithType(MapDeserializer.java:387)
    at com.fasterxml.jackson.databind.deser.impl.TypeWrappedDeserializer.deserialize(TypeWrappedDeserializer.java:42)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2833)
    at com.fasterxml.jackson.datatype.hibernate4.DefaultTypingFailsTest.testDefaultTypingFails(DefaultTypingFailsTest.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at junit.framework.TestCase.runTest(TestCase.java:168)
    at junit.framework.TestCase.runBare(TestCase.java:134)
    at junit.framework.TestResult$1.protect(TestResult.java:110)
    at junit.framework.TestResult.runProtected(TestResult.java:128)
    at junit.framework.TestResult.run(TestResult.java:113)
    at junit.framework.TestCase.run(TestCase.java:124)
    at junit.framework.TestSuite.runTest(TestSuite.java:243)
    at junit.framework.TestSuite.run(TestSuite.java:238)
    at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:253)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
@digulla
Copy link

digulla commented Jul 24, 2016

I can confirm this. It gets better when you use other values from ObjectMapper.DefaultTyping. The problem is that the serializer assumes that the deserialization code will pass the correct type.

I'm in a situation where I have to use mapper.readValue(json, Object.class); which would work if the serializer would always send a type. But as far as I could find out, there is no way to force this behavior. It eventually breaks for types which JSON directly supports like int[] or List<Integer> which are always serialized as [1,2,3].

My solution was to wrap the call to writeValueAsString(), intercept the types which Jackson doesn't handle correctly and call my own serialization code.

@cowtowncoder
Copy link
Member

@digulla Now that PersistentSet handling is improved (ideally serializer would know to downcast them to Sets, but that would have to be defined by module using some general facility, and such does not yet exist), I am wondering about your comment wrt int[] and List<Integer> -- I am not sure what you mean by that. Those should not be different from any other types, with respect to inclusion of type id information.

@digulla
Copy link

digulla commented Aug 24, 2016

Sorry, I mean Integer[] instead of List<Integer>. The list works.

For the array types, Jackson produces this JSON: [1,2,3] instead of ["[I", [1,2,3]] (for int[]). Same for Integer[] arrays which should start with the type "[Ljava.lang.Integer;".

My guess is that there is still some code lurking somewhere which catches types that JavaScript supports natively.

Suggestion: Add unit tests which serialize and deserialize all types (arrays and lists) or all kinds of bultin Java types. When you can serialize it, there should also be a way to deserialize it without telling Jackson the expected type.

@cowtowncoder
Copy link
Member

@digulla This sounds more like something related to core Jackson databinding.

As to type information addition is not hard-coded (there is nothing directly special about either arrays or Lists -- they are pluggable too), but the logic used to determine when type id is defined by choice of DefaultTyping argument.
Every choice of DefaultTyping has specific logic that determines for given Class whether type id should be written (and expected to be read) or not. Rules are documented in Javadocs, but it may be simpler to read the sources as it's about 25 lines of code.

So it is necessary to choose typing value that includes type you want to apply type id for.
Or, if none work right, implement custom TypeResolverBuilder which can implement exact custom rules, and call ObjectMapper.setDefaultTyping(resolverBuilder) to make mapper use it.

As to testing: there is plenty of testing for all kinds of types. But at this point extension of test coverage will happen by way of addressing reported problems, or via contributions. I am still not sure there is anything wrong wrt this handling, but if a test case can be provided, an issue for jackson-databind would make sense.

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