Джексон і загальний довідник про тип


107

Я хочу використовувати бібліотеку Джексона 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 об’єкт потрібно повернути ?. Наприклад: якщо я надіслав об’єкт типу Клієнт, тоді Клієнта слід повернути зі цього списку ?.

Дякую.


Будь ласка, додайте реалізацію getT ()
Джим Гаррісон

Це питання схоже на stackoverflow.com/questions/6062011/…, але вони запропонували вказати тип за допомогою TypeFactory. Однак я не знаю тип під час компіляції ...
techzen

TypeFactory має методи, які не потребують статичного класу; createCollectionType тощо.
StaxMan

Будь ласка, поділіться повним кодом. Я також стикаюся з тією ж проблемою.
AZ_

Чи не TypeReferenceабстрактно?
Кайл Делані

Відповіді:


194

Це добре відома проблема стирання типу Java: T - це лише змінна тип, і ви повинні вказати фактичний клас, як правило, як аргумент класу. Без такої інформації найкраще зробити межі; а звичайний T приблизно такий же, як "T поширює об'єкт". І тоді Джексон прив’яже об’єкти JSON як Карти.

У цьому випадку метод тестеру повинен мати доступ до класу, і ви можете його сконструювати

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

і потім

List<Foo> list = mapper.readValue(new File("input.json"), type);

16
Це працює: я зробив наступне: JavaType topMost = mapper.getTypeFactory (). ConstructParametricType (MyWrapper.class, ActualClassRuntime.class); а потім readValue, і нарешті спрацювало :)
techzen

Так, це спрацьовує - дякую за вказівку на метод створення загального типу, відмінного від типу Map / Collection!
StaxMan

1
@StaxMan чи краще зараз використовувати ClassMate для подібних речей?
хусайт

2
@husayt так, технічно ява-клас однокласника вищий. Але інтегрувати його з Джексоном трохи складно лише тому, що абстракція власного типу Джексона є інтегрованою частиною 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 ;
    }  
}

Як ініціалізувати TargetClass?
AZ_

Покажіть будь ласка невеликий приклад. Я передаю клас <?> Цілі, а потім отримую target.getClassName ().
AZ_

1
Додайте конструктор таким чином: JsonMarshallerUnmarshaller <T> {private Class <T> targetClass; JsonMarshallerUnmarshaller (клас <T> c) {targetClass = c; }} Внесіть відповідні зміни у функцію 'unmarshal', щоб використовувати цей клас, а не робити getClass скрізь.
rushidesai1

Пара приміток: код можна багато спростити, зазначивши, що всі винятки є підтипами IOException(потрібен лише один улов) і що інтроспектор анотації за замовчуванням вже є JacksonAnnotationIntrospector- тому не потрібно нічого робити ObjectMapper, просто побудувати його і він працює.
StaxMan

Тож цей код я навіть не збираюся компілювати. Є який-небудь живий приклад, щоб замість цього вставити?
Гайковий ключ

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"
}]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.