Jackson และการอ้างอิงประเภททั่วไป


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? ตัวอย่างเช่นหากฉันส่งวัตถุประเภทลูกค้าลูกค้าควรถูกส่งคืนจากรายการนั้น?

ขอบคุณ.


โปรดเพิ่มการใช้งาน getT ()
Jim Garrison

คำถามนี้คล้ายกับstackoverflow.com/questions/6062011/…แต่พวกเขาแนะนำให้ระบุประเภทโดยใช้ TypeFactory อย่างไรก็ตามฉันไม่ทราบประเภทในเวลารวบรวม ...
techzen

TypeFactory มีวิธีการที่ไม่ต้องการคลาสคงที่ createCollectionType และอื่น ๆ
StaxMan

กรุณาแบ่งปันรหัสที่สมบูรณ์ ฉันก็ประสบปัญหาเดียวกัน
AZ_

TypeReferenceนามธรรมไม่ใช่เหรอ
Kyle Delaney

คำตอบ:


195

นี่เป็นปัญหาที่ทราบกันดีกับการลบประเภท Java: T เป็นเพียงตัวแปรประเภทและคุณต้องระบุคลาสจริงโดยปกติจะเป็นอาร์กิวเมนต์คลาส หากไม่มีข้อมูลดังกล่าวสิ่งที่ดีที่สุดที่สามารถทำได้คือใช้ขอบเขต และ T ธรรมดาจะเหมือนกับ 'T ขยายวัตถุ' จากนั้น Jackson จะผูก JSON Objects เป็นแผนที่

ในกรณีนี้วิธีทดสอบต้องมีสิทธิ์เข้าถึง Class และคุณสามารถสร้างได้

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

แล้ว

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

16
ได้ผล: ฉันทำสิ่งต่อไปนี้: JavaType topMost = mapper.getTypeFactory (). constructionParametricType (MyWrapper.class, ActualClassRuntime.class); จากนั้นก็ readValue และในที่สุดก็ใช้งานได้ :)
techzen

ใช่มันได้ผล - ขอบคุณที่ชี้ให้เห็นวิธีการสร้างประเภททั่วไปนอกเหนือจากประเภทแผนที่ / คอลเลคชัน!
StaxMan

1
@StaxMan จะดีกว่าไหมหากใช้ ClassMate สำหรับสิ่งเหล่านี้นับจากนี้
Husayt

2
ใช่ใช่แล้วในทางเทคนิค java-classmate lib นั้นเหนือกว่า แต่การรวมเข้ากับ Jackson นั้นค่อนข้างยุ่งยากเพียงเพราะสิ่งที่เป็นนามธรรมของ Jackson เองนั้นรวมอยู่ในส่วนหนึ่งของ API ในระยะยาวจะเป็นการดีที่จะหาวิธีที่เหมาะสมในการทำให้แจ็คสันใช้รหัสเพื่อนร่วมชั้นไม่ว่าจะฝังตัวหรือผ่านทางแผนก
StaxMan

1
ฉันรู้สึกว่าแจ็คสันไม่ควรต้องปกปิดสิ่งที่รู้สึกเหมือนมีช่องว่างในยาชื่อสามัญ แต่อย่างใดมันก็ทำได้ดีมาก
Adrian Baker

6

'JavaType' ใช้งานได้ !! ฉันพยายาม unmarshall (deserialize) List ใน json String ไปยัง ArrayList java Objects และพยายามหาวิธีแก้ไขมาหลายวัน
ด้านล่างนี้คือรหัสที่ให้คำตอบแก่ฉันในที่สุด รหัส:

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(ต้องการเพียงตัวจับเดียว) และ introspector คำอธิบายประกอบเริ่มต้นนั้นมีอยู่แล้ว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"
}]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.