ฉันมีสตริง json ซึ่งฉันควร deSerialize ในชั้นเรียนต่อไปนี้
class Data <T> {
int found;
Class<T> hits
}
ฉันต้องทำอย่างไร? นี่เป็นวิธีปกติ
mapper.readValue(jsonString, Data.class);
แต่ฉันจะพูดถึงสิ่งที่ T หมายถึงได้อย่างไร
ฉันมีสตริง json ซึ่งฉันควร deSerialize ในชั้นเรียนต่อไปนี้
class Data <T> {
int found;
Class<T> hits
}
ฉันต้องทำอย่างไร? นี่เป็นวิธีปกติ
mapper.readValue(jsonString, Data.class);
แต่ฉันจะพูดถึงสิ่งที่ T หมายถึงได้อย่างไร
คำตอบ:
คุณต้องสร้างTypeReference
วัตถุสำหรับประเภทสามัญแต่ละประเภทที่คุณใช้และใช้สำหรับการดีซีเรียลไลเซชัน ตัวอย่างเช่น -
mapper.readValue(jsonString, new TypeReference<Data<String>>() {});
Data<T>
นั่นไม่ใช่ประเภท คุณต้องระบุคลาสจริง Data<Object>
มิฉะนั้นจะเป็นเช่นเดียวกับ
TypeReference
คืออะไร มันคือcom.fasterxml.jackson.core.type
อะไร
Data<MyType>
คุณไม่สามารถทำเช่นนั้นคุณจะต้องระบุประเภทการแก้ไขอย่างเต็มที่เช่น T
เป็นเพียงตัวแปรและไม่มีความหมาย
แต่ถ้าคุณหมายถึงสิ่งนั้นT
จะเป็นที่รู้จักไม่ใช่แบบคงที่คุณต้องสร้างสิ่งที่เทียบเท่ากับTypeReference
แบบไดนามิก คำถามอื่น ๆ ที่อ้างถึงอาจพูดถึงเรื่องนี้แล้ว แต่ควรมีลักษณะดังนี้:
public Data<T> read(InputStream json, Class<T> contentClass) {
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, contentClass);
return mapper.readValue(json, type);
}
TypeReference
: return mapper.readValue(json, clazz);
ปัญหาที่นี่คืออะไร?
TypeFactory
.. ฉันจะแก้ไขคำตอบของฉัน
สิ่งแรกที่คุณทำคือทำให้เป็นอันดับจากนั้นคุณสามารถทำการดีซีเรียลไลซ์ได้
ดังนั้นเมื่อคุณทำให้เป็นอันดับคุณควรใช้@JsonTypeInfo
เพื่อให้แจ็คสันเขียนข้อมูลระดับลงในข้อมูล json ของคุณ สิ่งที่คุณสามารถทำได้คือ:
Class Data <T> {
int found;
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="@class")
Class<T> hits
}
จากนั้นเมื่อคุณหยุดการปล่อยออกมาคุณจะพบว่าแจ็คสันได้ทำการแยกข้อมูลของคุณลงในคลาสที่ตัวแปรของคุณนิยมใช้งานจริงนั้นเป็นรันไทม์
สำหรับคลาสข้อมูล <>
ObjectMapper mapper = new ObjectMapper();
JavaType type = mapper.getTypeFactory().constructParametrizedType(Data.class, Data.class, Parameter.class);
Data<Parameter> dataParam = mapper.readValue(jsonString,type)
แค่เขียนเมธอดสแตติกในคลาส Util ฉันกำลังอ่าน Json จากไฟล์ คุณสามารถกำหนดให้ String อ่านค่าได้เช่นกัน
public static <T> T convertJsonToPOJO(String filePath, Class<?> target) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(new File(filePath), objectMapper .getTypeFactory().constructCollectionType(List.class, Class.forName(target.getName())));
}
การใช้งาน:
List<TaskBean> list = Util.<List<TaskBean>>convertJsonToPOJO("E:/J2eeWorkspaces/az_workspace_svn/az-client-service/dir1/dir2/filename.json", TaskBean.class);
คุณสามารถพันมันในอีกชั้นหนึ่งซึ่งรู้ประเภททั่วไปของคุณ
เช่น,
class Wrapper {
private Data<Something> data;
}
mapper.readValue(jsonString, Wrapper.class);
นี่คือบางสิ่งบางอย่างเป็นรูปธรรม คุณต้องใช้ wrapper ต่อประเภท reified มิฉะนั้นแจ็กสันไม่ทราบว่าจะสร้างวัตถุอะไร
สตริง JSON ที่ต้อง deserialized T
จะต้องมีข้อมูลเกี่ยวกับชนิดพารามิเตอร์
คุณจะต้องใส่คำอธิบายประกอบแจ็คสันในทุกชั้นที่สามารถส่งเป็นพารามิเตอร์T
ไปยังคลาสData
เพื่อให้ข้อมูลประเภทเกี่ยวกับประเภทพารามิเตอร์T
สามารถอ่าน / เขียนจากสตริง JSON โดยแจ็คสัน
ขอให้เราคิดว่าอาจจะเป็นระดับใดที่ขยายระดับนามธรรมT
Result
class Data <T extends Result> {
int found;
Class<T> hits
}
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.WRAPPER_OBJECT)
@JsonSubTypes({
@JsonSubTypes.Type(value = ImageResult.class, name = "ImageResult"),
@JsonSubTypes.Type(value = NewsResult.class, name = "NewsResult")})
public abstract class Result {
}
public class ImageResult extends Result {
}
public class NewsResult extends Result {
}
เมื่อแต่ละคลาส (หรือ supertype ทั่วไปของพวกเขา) ที่สามารถส่งผ่านเป็นT
หมายเหตุประกอบพารามิเตอร์Jackson จะรวมข้อมูลเกี่ยวกับพารามิเตอร์T
ใน JSON จากนั้น JSON นั้นสามารถทำการดีซีเรียลไลซ์ได้โดยไม่ต้องรู้พารามิเตอร์T
ในเวลาคอมไพล์ ลิงก์เอกสารคู่มือของแจ็คสัน
นี้พูดถึง Polerorphic Deserialization แต่มีประโยชน์ในการอ้างถึงคำถามนี้เช่นกัน
จาก Jackson 2.5 เป็นวิธีที่ยอดเยี่ยมในการแก้ปัญหาโดยใช้
TypeFactory.constructParametricType (Class parametrized, Class ... parameterClasses)วิธีการที่ช่วยให้กำหนด Jackson แบบ straigthly JavaType
โดยการระบุ classized พารามิเตอร์และประเภทของพารามิเตอร์
หากว่าคุณต้องการยกเลิกการซีเรียลData<String>
เนลคุณสามารถทำสิ่งต่อไปนี้
// the json variable may be a String, an InputStream and so for...
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class);
Data<String> data = mapper.readValue(json, type);
โปรดทราบว่าหากคลาสประกาศหลายประเภทที่กำหนดพารามิเตอร์มันจะไม่ยากขึ้นจริงๆ:
class Data <T, U> {
int found;
Class<T> hits;
List<U> list;
}
เราทำได้:
JavaType type = mapper.getTypeFactory().constructParametricType(Data.class, String.class, Integer);
Data<String, Integer> data = mapper.readValue(json, type);
public class Data<T> extends JsonDeserializer implements ContextualDeserializer {
private Class<T> cls;
public JsonDeserializer createContextual(DeserializationContext ctx, BeanProperty prop) throws JsonMappingException {
cls = (Class<T>) ctx.getContextualType().getRawClass();
return this;
}
...
}
หากคุณใช้สกาล่าและรู้ประเภททั่วไปในเวลารวบรวม แต่ไม่ต้องการส่ง TypeReference ด้วยตนเองทุกที่ในทนายความของคุณคุณสามารถใช้รหัสต่อไปนี้ (ด้วยแจ็คสัน 2.9.5):
def read[T](entityStream: InputStream)(implicit typeTag: WeakTypeTag[T]): T = {
//nathang: all of this *crazy* scala reflection allows us to handle List[Seq[Map[Int,Value]]]] without passing
// new TypeReference[List[Seq[Map[Int,Value]]]]](){} to the function
def recursiveFindGenericClasses(t: Type): JavaType = {
val current = typeTag.mirror.runtimeClass(t)
if (t.typeArgs.isEmpty) {
val noSubtypes = Seq.empty[Class[_]]
factory.constructParametricType(current, noSubtypes:_*)
}
else {
val genericSubtypes: Seq[JavaType] = t.typeArgs.map(recursiveFindGenericClasses)
factory.constructParametricType(current, genericSubtypes:_*)
}
}
val javaType = recursiveFindGenericClasses(typeTag.tpe)
json.readValue[T](entityStream, javaType)
}
ซึ่งสามารถใช้ดังนี้:
read[List[Map[Int, SomethingToSerialize]]](inputStream)
ในการยกเลิกการวางจำหน่าย JSON-string ทั่วไปไปยัง Java-object ด้วย Jackson คุณต้อง:
เพื่อกำหนดคลาส JSON
ทำการแมปแอตทริบิวต์
รหัสสุดท้ายทดสอบและพร้อมใช้:
static class MyJSON {
private Map<String, Object> content = new HashMap<>();
@JsonAnySetter
public void setContent(String key, Object value) {
content.put(key, value);
}
}
String json = "{\"City\":\"Prague\"}";
try {
MyPOJO myPOJO = objectMapper.readValue(json, MyPOJO.class);
String jsonAttVal = myPOJO.content.get("City").toString();
System.out.println(jsonAttVal);
} catch (IOException e) {
e.printStackTrace();
}
สำคัญ:
@JsonAnySetter
คำอธิบายประกอบเป็นสิ่งที่จำเป็นต้องมีเพื่อให้แน่ใจว่ามีการแยกวิเคราะห์ JSON และประชากรทั่วไป
สำหรับกรณีที่ซับซ้อนมากขึ้นด้วยอาร์เรย์ที่ซ้อนกันโปรดดูการอ้างอิงของ Baeldung: https://www.baeldung.com/jackson-mapping-dynamic-object
ตัวอย่างของการตัดสินใจที่ไม่ดี แต่เรียบง่าย (ไม่เพียง แต่สำหรับแจ็คสันเท่านั้นและสำหรับ Spring RestTemplate ฯลฯ ):
Set<MyClass> res = new HashSet<>();
objectMapper.readValue(json, res.getClass());