Jackson và tham chiếu loại chung


107

Tôi muốn sử dụng thư viện json jackson cho một phương thức chung như sau:

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);
     }
}

Bây giờ, vấn đề là khi tôi gọi getMyObject () bên trong đối tượng yêu cầu, jackson sẽ trả về đối tượng tùy chỉnh lồng nhau dưới dạng LinkedHashMap. Có cách nào mà tôi chỉ định rằng đối tượng T cần được trả lại không ?. Ví dụ: nếu tôi đã gửi đối tượng loại Khách hàng thì Khách hàng sẽ được trả lại từ Danh sách đó ?.

Cảm ơn.


Vui lòng thêm việc thực hiện Gett ()
Jim Garrison

Câu hỏi này tương tự như stackoverflow.com/questions/6062011/… nhưng họ đã đề xuất chỉ định loại bằng TypeFactory. Tuy nhiên tôi không biết loại tại thời gian biên dịch ...
techzen

TypeFactory có các phương thức không cần lớp tĩnh; createCollectionType, v.v.
StaxMan

Vui lòng chia sẻ mã hoàn chỉnh. Tôi cũng đang phải đối mặt với vấn đề tương tự.
AZ_

Không TypeReferencetrừu tượng?
Kyle Delaney

Câu trả lời:


194

Đây là một vấn đề nổi tiếng với tính năng xóa kiểu Java: T chỉ là một biến kiểu và bạn phải chỉ ra lớp thực tế, thường là đối số Lớp. Nếu không có thông tin như vậy, tốt nhất có thể làm là sử dụng giới hạn; và T đơn giản gần giống với 'T kéo dài Đối tượng'. Và Jackson sau đó sẽ ràng buộc các Đối tượng JSON dưới dạng Bản đồ.

Trong trường hợp này, phương thức người kiểm tra cần có quyền truy cập vào Lớp và bạn có thể xây dựng

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

và sau đó

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

16
Nó hoạt động: Tôi đã làm như sau: JavaType topMost = mapper.getTypeFactory (). ConstructParametricType (MyWrapper.class, ActualClassRuntime.class); và sau đó đã làm các readValue và cuối cùng làm việc :)
techzen

Vâng, điều đó hoạt động - cảm ơn bạn đã chỉ ra phương pháp tạo kiểu chung khác với kiểu Bản đồ / Bộ sưu tập!
StaxMan

1
@StaxMan có nên sử dụng ClassMate cho những việc này từ bây giờ không?
husayt

2
@husayt vâng, lib java-classmate về mặt kỹ thuật là vượt trội. Nhưng việc tích hợp nó với Jackson hơi phức tạp chỉ vì kiểu trừu tượng của riêng Jackson được tích hợp một phần của API. Về lâu dài, sẽ thật tuyệt nếu tìm ra cách thích hợp để khiến Jackson sử dụng mã bạn cùng lớp, được nhúng hoặc thông qua dep.
StaxMan

1
Tôi cảm thấy như Jackson không cần phải che đậy những gì cảm thấy giống như những khoảng trống trong generic, nhưng dù bằng cách nào thì nó cũng làm rất tốt.
Adrian Baker

6

'JavaType' hoạt động !! Tôi đang cố gắng bỏ quản lý (deserialize) một Danh sách trong json String thành ArrayList java Objects và đã vật lộn để tìm ra giải pháp kể từ nhiều ngày.
Dưới đây là mã cuối cùng đã cho tôi giải pháp. Mã:

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 ;
    }  
}

Làm thế nào để khởi tạo TargetClass?
AZ_

Xin chỉ cho tôi một ví dụ nhỏ. Tôi đang vượt qua mục tiêu <?> Class và sau đó nhận target.getClassName ().
AZ_

1
Thêm một hàm tạo như sau: JsonMarshallerUnmarshaller <T> {private Class <T> targetClass; JsonMarshallerUnmarshaller (Lớp <T> c) {targetClass = c; }} Thực hiện các thay đổi thích hợp ngay bây giờ đối với hàm 'unmarshal' để sử dụng lớp này thay vì thực hiện getClass ở mọi nơi.
rushidesai1

Một số lưu ý: mã có thể được đơn giản hóa rất nhiều bằng cách lưu ý rằng tất cả các ngoại lệ đều là các kiểu con của IOException(chỉ cần một lần bắt), và trình kiểm tra chú thích mặc định đã có JacksonAnnotationIntrospector- vì vậy không cần phải làm gì cả ObjectMapper, chỉ cần xây dựng nó và nó hoạt động.
StaxMan

Vì vậy, mã này tôi thậm chí không thể biên dịch. Có bất kỳ ví dụ trực tiếp nào để dán vào không?
Cờ lê

0

Tôi đã sửa đổi câu trả lời của rushidesai1 để bao gồm một ví dụ hoạt động.

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
    }
}

Đầu ra

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"
}]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.