Джексон и ссылка на универсальный тип

107

Я хочу использовать библиотеку jackson json для общего метода следующим образом:

public MyRequest<T> tester() {
    TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();  
    MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
    return requestWrapper.getRequest();
}

...

public class MyWrapper<T> {

    private MyRequest<T> request;

    public MyRequest<T> getRequest() {
        return request;
    }

    public void setRequest(MyRequest<T> request) {
        this.request = request;
    }
}


 public class MyRequest{
     private List<T> myobjects;

     public void setMyObjects(List<T> ets) {
         this.myobjects = ets;
     }

     @NotNull
     @JsonIgnore
     public T getMyObject() {
         return myobjects.get(0);
     }
}

Теперь проблема в том, что когда я вызываю getMyObject (), который находится внутри объекта запроса, Джексон возвращает вложенный настраиваемый объект как LinkedHashMap. Есть ли способ указать, что объект T должен быть возвращен? Например: если я отправил объект типа Customer, тогда Customer должен быть возвращен из этого списка ?.

Спасибо.

Techzen
источник
Пожалуйста, добавьте реализацию getT ()
Джим Гаррисон
Этот вопрос похож на stackoverflow.com/questions/6062011/…, но они предложили указать тип с помощью TypeFactory. Однако я не знаю тип во время компиляции ...
techzen
TypeFactory имеет методы, которым не нужен статический класс; createCollectionType и так далее.
StaxMan
Пожалуйста, поделитесь полным кодом. Я тоже столкнулся с той же проблемой.
AZ_
Разве не TypeReferenceабстрактно?
Кайл Делани,

Ответы:

196

Это хорошо известная проблема со стиранием типа Java: T - это просто переменная типа, и вы должны указать фактический класс, обычно как аргумент класса. Без такой информации лучшее, что можно сделать, - это использовать границы; а простой T примерно такой же, как «T extends Object». Затем Джексон привяжет объекты JSON как карты.

В этом случае метод тестера должен иметь доступ к классу, и вы можете построить

JavaType type = mapper.getTypeFactory().
  constructCollectionType(List.class, Foo.class)

а потом

List<Foo> list = mapper.readValue(new File("input.json"), type);
StaxMan
источник
16
Это работает: я сделал следующее: JavaType topMost = mapper.getTypeFactory (). ConstructParametricType (MyWrapper.class, ActualClassRuntime.class); а затем выполнил readValue, и это, наконец, сработало :)
techzen
Да, это работает - спасибо, что указали на метод создания универсального типа, отличный от типа Map / Collection!
StaxMan
1
@StaxMan, не лучше ли теперь использовать ClassMate для подобных вещей?
Husayt
2
@husayt да, технически java-classmate lib лучше. Но интегрировать его с Джексоном немного сложно просто потому, что собственная абстракция типов Джексона является интегрированной частью API. В долгосрочной перспективе было бы здорово найти правильный способ заставить Джексона использовать код одноклассника, встроенный или через dep.
StaxMan
1
Я чувствую, что Джексону не следует закрывать то, что кажется пробелами в дженериках, но в любом случае он делает это очень хорошо.
Адриан Бейкер
6

«JavaType» работает !! Я пытался демаршалировать (десериализовать) список в json String для Java-объектов ArrayList и пытался найти решение несколько дней.
Ниже приведен код, который наконец дал мне решение. Код:

JsonMarshallerUnmarshaller<T> {
    T targetClass;

    public ArrayList<T> unmarshal(String jsonString) {
        ObjectMapper mapper = new ObjectMapper();

        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
        mapper.getDeserializationConfig()
            .withAnnotationIntrospector(introspector);

        mapper.getSerializationConfig()
            .withAnnotationIntrospector(introspector);
        JavaType type = mapper.getTypeFactory().
            constructCollectionType(
                ArrayList.class, 
                targetclass.getClass());

        try {
            Class c1 = this.targetclass.getClass();
            Class c2 = this.targetclass1.getClass();
            ArrayList<T> temp = (ArrayList<T>) 
                mapper.readValue(jsonString,  type);
            return temp ;
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null ;
    }  
}
rushidesai1
источник
Как инициализировать TargetClass?
AZ_
Покажите мне небольшой пример. Я передаю цель Class <?>, А затем получаю target.getClassName ().
AZ_
1
Добавьте конструктор следующим образом: JsonMarshallerUnmarshaller <T> {private Class <T> targetClass; JsonMarshallerUnmarshaller (Класс <T> c) {targetClass = c; }} Внесите соответствующие изменения в функцию unmarshal, чтобы использовать этот класс вместо повсеместного использования getClass.
rushidesai1
Пара примечаний: код можно значительно упростить, отметив, что все исключения являются подтипами IOException(нужен только один улов) и что интроспектор аннотаций по умолчанию уже есть JacksonAnnotationIntrospector- поэтому не нужно ничего делать ObjectMapper, просто создайте его, и он работает.
StaxMan
Так что этот код я даже не могу скомпилировать. Есть какой-нибудь живой пример для вставки?
Wrench
0

Я изменил ответ rushidesai1 чтобы включить рабочий пример.

JsonMarshaller.java

import java.io.*;
import java.util.*;

public class JsonMarshaller<T> {
    private static ClassLoader loader = JsonMarshaller.class.getClassLoader();

    public static void main(String[] args) {
        try {
            JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
            String jsonString = read(loader.getResourceAsStream("data.json"));
            List<Station> stations = marshaller.unmarshal(jsonString);
            stations.forEach(System.out::println);
            System.out.println(marshaller.marshal(stations));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("resource")
    public static String read(InputStream ios) {
        return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
    }
}

Вывод

Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]

JsonMarshallerUnmarshaller.java

import java.io.*;
import java.util.List;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

public class JsonMarshallerUnmarshaller<T> {
    private ObjectMapper mapper;
    private Class<T> targetClass;

    public JsonMarshallerUnmarshaller(Class<T> targetClass) {
        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

        mapper = new ObjectMapper();
        mapper.getDeserializationConfig().with(introspector);
        mapper.getSerializationConfig().with(introspector);

        this.targetClass = targetClass;
    }

    public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
        return parseList(jsonString, mapper, targetClass);
    }

    public String marshal(List<T> list) throws JsonProcessingException {
        return mapper.writeValueAsString(list);
    }

    public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
            throws JsonParseException, JsonMappingException, IOException {
        return mapper.readValue(str, listType(mapper, clazz));
    }

    public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
            throws JsonParseException, JsonMappingException, IOException {
        return mapper.readValue(is, listType(mapper, clazz));
    }

    public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
        return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
    }
}

Station.java

public class Station {
    private long id;
    private String title;
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
    }
}

data.json

[{
  "id": 123,
  "title": "my title",
  "name": "my name"
}, {
  "id": 456,
  "title": "my title 2",
  "name": "my name 2"
}]
Мистер Поливирл
источник