Re: [jackson-user] Construct type for class implementing List bound to specific generic type

2017-02-03 Thread thorben . lindhauer
Hi Tatu,

Thanks for your response and good to know that I am using something that 
isn't intended to be used :)

I'll elaborate a little bit on my actual use case and then ask some 
follow-up questions.

My use case is: I'd like to have code that takes instances of classes 
implementing java.util.List, serializes them as JSON and stores the JSON 
along with the instances type name such that I can deserialize the list at 
a later point in time. I'd like the JSON to contain as little type-metadata 
as possible. For a regular java.util.ArrayList I use this code:

List someList = ..;
String typeName;
if (!someList.isEmpty()) {
  Object firstElement = list.get(0);
  typeName = typeFactory.constructCollectionType(someList.getClass(), 
firstElement);
}
else
{
  typeName = typeFactory.constructType(someList.getClass()); // or could 
simply use Class#getName
}

This code circumvents the type erasure problem. Of course it has the 
limitations that it cannot deal with lists that contain instances of 
different classes or lists that contain lists again. These limitations are 
ok for my use case.

For deserialization, I use the aforementioned `constructFromCanonical()`. 

With jackson-databind 2.6.3, this code works fine for both instances of 
java.util.ArrayList or classes like WrapperList in my first post. With 
Jackson 2.8.6 this approach fails for WrapperList.

Now my questions would be:

1. Can my use case (serialization of JSON, serialization of type, avoiding 
type information in JSON) be solved with Jackson end-user features?
2. If no to 1), would I have to lift the restriction of avoiding type 
information in the JSON?
3. If in general yes to 1), what would be the end-user way to get from a 
canonical type string and JSON back to a deserialized object?

Cheers,
Thorben

Am Donnerstag, 2. Februar 2017 17:21:32 UTC+1 schrieb Tatu Saloranta:
>
> Ok, code has a few problems. 
>
> First: method `constructFromCanonical()` really isn't meant to be 
> end-user functionality. 
> Maybe Javadocs should make this clear, but it is not something I was 
> planning to be used by anything but core Jackson functionality. I can 
> sort of see why it might seem useful, but there is one big problem 
> using it for persisting generic type... 
>
> The problem is this: all instances of generic types, at runtime, are 
> subject to type erasure. 
> So you will not be able to obtain any more information than actual 
> underlying `Class`. 
> This being the case you might as well just access `class.getName()` 
> and use that. 
> This is different from programmatically constructed `JavaType` 
> instances which can and do retain generic type information; but this 
> comes from property type (and class super type) declarations. 
>
> So use of: 
>
>  JavaType type = typeFactory.constructType(list.getClass()); 
>
> tends not to work as expected: none of generic types are preserved. 
>
> In this case it is also possible that canonical name handling isn't 
> working like it should (maybe worth filing a bug for), since it should 
> always be possible to read back canonical name you created. 
> However, I don't think it would work for your use case: it can not be 
> any better than simply serializing name of the `Class` you have, due 
> to type erasure. 
>
> Going back to persisting type externally: you should either serialize 
> simple class name (safest), or, if you absolutely need generic typing, 
> programmatically construct generic type using `TypeFactory`, and then 
> get canonical name. 
>
> I hope this helps, 
>
> -+ Tatu +- 
>
>
> On Tue, Jan 31, 2017 at 2:18 AM,   
> wrote: 
> > Hi, 
> > 
> > Jackson-databind version: 2.8.6 
> > 
> > I have a class that implements java.util.List with a compile-time 
> defined 
> > generic type, e.g. 
> > 
> > public static class WrapperList implements List 
> > { 
> >// delegate to methods of a wrapped ArrayList 
> > } 
> > 
> > Now I want to use TypeFactory to create a canonical String 
> representation of 
> > that type that I can persist and later use for (de-)serialization, e.g. 
> > 
> > WrapperList list = new WrapperList(); 
> > list.add((byte) 10); 
> > 
> > ObjectMapper mapper = new ObjectMapper(); 
> > TypeFactory typeFactory = mapper.getTypeFactory(); 
> > JavaType type = typeFactory.constructType(list.getClass()); 
> > String canonicalTypeName = type.toCanonical(); 
> > 
> > This results in canonicalTypeName being 
> > org.camunda.bpm.Main$WrapperList. However, calling 
> > 
> > typeFactory.constructFromCanonical(canonicalTypeName); 
> > 
> > results in the following exception: 
> > 
> > Exception in thread "main" java.lang.IllegalArgumentException: Can not 
> > create TypeBindings for class org.camunda.bpm.Main$WrapperList with 1 
> type 
> > parameter: class expects 0 
> > at 
> > 
> com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:125)
>  
>
> > at 
> > 
> 

Re: [jackson-user] Construct type for class implementing List bound to specific generic type

2017-02-02 Thread Tatu Saloranta
Ok, code has a few problems.

First: method `constructFromCanonical()` really isn't meant to be
end-user functionality.
Maybe Javadocs should make this clear, but it is not something I was
planning to be used by anything but core Jackson functionality. I can
sort of see why it might seem useful, but there is one big problem
using it for persisting generic type...

The problem is this: all instances of generic types, at runtime, are
subject to type erasure.
So you will not be able to obtain any more information than actual
underlying `Class`.
This being the case you might as well just access `class.getName()`
and use that.
This is different from programmatically constructed `JavaType`
instances which can and do retain generic type information; but this
comes from property type (and class super type) declarations.

So use of:

 JavaType type = typeFactory.constructType(list.getClass());

tends not to work as expected: none of generic types are preserved.

In this case it is also possible that canonical name handling isn't
working like it should (maybe worth filing a bug for), since it should
always be possible to read back canonical name you created.
However, I don't think it would work for your use case: it can not be
any better than simply serializing name of the `Class` you have, due
to type erasure.

Going back to persisting type externally: you should either serialize
simple class name (safest), or, if you absolutely need generic typing,
programmatically construct generic type using `TypeFactory`, and then
get canonical name.

I hope this helps,

-+ Tatu +-


On Tue, Jan 31, 2017 at 2:18 AM,   wrote:
> Hi,
>
> Jackson-databind version: 2.8.6
>
> I have a class that implements java.util.List with a compile-time defined
> generic type, e.g.
>
> public static class WrapperList implements List
> {
>// delegate to methods of a wrapped ArrayList
> }
>
> Now I want to use TypeFactory to create a canonical String representation of
> that type that I can persist and later use for (de-)serialization, e.g.
>
> WrapperList list = new WrapperList();
> list.add((byte) 10);
>
> ObjectMapper mapper = new ObjectMapper();
> TypeFactory typeFactory = mapper.getTypeFactory();
> JavaType type = typeFactory.constructType(list.getClass());
> String canonicalTypeName = type.toCanonical();
>
> This results in canonicalTypeName being
> org.camunda.bpm.Main$WrapperList. However, calling
>
> typeFactory.constructFromCanonical(canonicalTypeName);
>
> results in the following exception:
>
> Exception in thread "main" java.lang.IllegalArgumentException: Can not
> create TypeBindings for class org.camunda.bpm.Main$WrapperList with 1 type
> parameter: class expects 0
> at
> com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:125)
> at
> com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:95)
> at
> com.fasterxml.jackson.databind.type.TypeBindings.create(TypeBindings.java:86)
> at
> com.fasterxml.jackson.databind.type.TypeParser.parseType(TypeParser.java:54)
> at
> com.fasterxml.jackson.databind.type.TypeParser.parse(TypeParser.java:33)
> at
> com.fasterxml.jackson.databind.type.TypeFactory.constructFromCanonical(TypeFactory.java:544)
> at com.example.Main.main(Main.java:42)
>
> Am I using the wrong method for creating the JavaType in this case?
>
> Cheers,
> Thorben
>
> PS: Here's the full code example for reference:
>
> package com.example;
>
> import java.io.IOException;
> import java.util.ArrayList;
> import java.util.Collection;
> import java.util.Iterator;
> import java.util.List;
> import java.util.ListIterator;
>
> import com.fasterxml.jackson.databind.JavaType;
> import com.fasterxml.jackson.databind.ObjectMapper;
> import com.fasterxml.jackson.databind.type.TypeFactory;
>
> public class Main {
>
>   public static void main(String[] args) throws IOException {
> WrapperList list = new WrapperList();
> list.add((byte) 10);
>
> ObjectMapper mapper = new ObjectMapper();
> TypeFactory typeFactory = mapper.getTypeFactory();
> JavaType type = typeFactory.constructType(list.getClass());
> System.out.println(type.toCanonical());
>
> JavaType reconstructedType =
> typeFactory.constructFromCanonical(type.toCanonical());
>   }
>
>   public static class WrapperList implements List
>   {
> protected List innerList = new ArrayList();
>
> public int size() {
>   return innerList.size();
> }
>
>
> public boolean isEmpty() {
>   return innerList.isEmpty();
> }
>
>
> public boolean contains(Object o) {
>   return innerList.contains(o);
> }
>
>
> public Iterator iterator() {
>   return innerList.iterator();
> }
>
>
> public Object[] toArray() {
>   return innerList.toArray();
> }
>
>
> public  T[] toArray(T[] a) {
>   return innerList.toArray(a);
> }
>
>
> public boolean add(Byte e) {
>   return innerList.add(e);
> }
>
>
>