2010-09-02 50 views
32

Tôi có một đối tượng cây ở định dạng JSON Tôi đang cố gắng deserialize với Gson. Mỗi nút chứa các nút con của nó như là các trường của kiểu đối tượng Nút. Nút là một giao diện, trong đó có một số triển khai lớp cụ thể. Trong quá trình deserialization, làm thế nào tôi có thể giao tiếp với Gson mà lớp cụ thể để thực hiện khi deserializing nút, nếu tôi không biết một ưu tiên mà gõ nút thuộc về? Mỗi nút có một trường thành viên xác định loại. Có cách nào để truy cập vào trường khi đối tượng ở dạng tuần tự, và bằng cách nào đó truyền đạt kiểu này sang Gson?Deserializing một lớp trừu tượng trong Gson

Cảm ơn!

+0

Hãy xem [http://stackoverflow.com/a/22081826/3315914] (http://stackoverflow.com/a/22081826/3315914) – rpax

Trả lời

40

tôi muốn đề nghị thêm một tùy chỉnh JsonDeserializer cho Node s:

Gson gson = new GsonBuilder() 
    .registerTypeAdapter(Node.class, new NodeDeserializer()) 
    .create(); 

Bạn sẽ có thể truy cập vào JsonElement đại diện cho các nút trong phương pháp của deserializer, chuyển đổi đó để một JsonObject, và lấy lĩnh vực mà chỉ định loại. Sau đó bạn có thể tạo một thể hiện của loại chính xác của Node dựa trên đó.

+0

Điều đó hoạt động hoàn hảo. Cảm ơn! – user437480

+0

@ eng-carlin Đó là phong tục để upvote và chấp nhận một câu trả lời nếu nó làm việc cho bạn, vì vậy nếu bạn có thể làm điều đó tôi sẽ đánh giá cao nó. – ColinD

+0

Dấu chọn màu xanh lá cây đã được nhấp. Tôi muốn upvote bạn, nhưng danh tiếng của tôi là không đủ cao: ( – user437480

22

Bạn cần đăng ký cả JSONSerializer và JSONDeserializer. Ngoài ra, bạn có thể triển khai bộ điều hợp chung cho tất cả các giao diện của mình theo cách sau:

  • Trong quá trình tuần tự hóa: Thêm thông tin META của loại lớp impl thực tế.
  • Trong deserialization: Lấy rằng thông tin meta và gọi JSONDeserailize đó lớp

Đây là việc thực hiện mà tôi đã sử dụng cho bản thân mình và hoạt động tốt.

public class PropertyBasedInterfaceMarshal implements 
     JsonSerializer<Object>, JsonDeserializer<Object> { 

    private static final String CLASS_META_KEY = "CLASS_META_KEY"; 

    @Override 
    public Object deserialize(JsonElement jsonElement, Type type, 
      JsonDeserializationContext jsonDeserializationContext) 
      throws JsonParseException { 
     JsonObject jsonObj = jsonElement.getAsJsonObject(); 
     String className = jsonObj.get(CLASS_META_KEY).getAsString(); 
     try { 
      Class<?> clz = Class.forName(className); 
      return jsonDeserializationContext.deserialize(jsonElement, clz); 
     } catch (ClassNotFoundException e) { 
      throw new JsonParseException(e); 
     } 
    } 

    @Override 
    public JsonElement serialize(Object object, Type type, 
      JsonSerializationContext jsonSerializationContext) { 
     JsonElement jsonEle = jsonSerializationContext.serialize(object, object.getClass()); 
     jsonEle.getAsJsonObject().addProperty(CLASS_META_KEY, 
       object.getClass().getCanonicalName()); 
     return jsonEle; 
    } 

} 

Sau đó, bạn có thể đăng ký chuyển đổi này cho tất cả các giao diện của bạn như sau

Gson gson = new GsonBuilder() 
     .registerTypeAdapter(IInterfaceOne.class, 
       new PropertyBasedInterfaceMarshal()) 
     .registerTypeAdapter(IInterfaceTwo.class, 
       new PropertyBasedInterfaceMarshal()).create(); 
+0

ý nghĩa của bạn là gì bởi IInterfaceOne.class và IInterfaceTwo.class –

+0

Cảm ơn bạn đã cung cấp giải pháp thanh lịch/tái sử dụng nhất cho vấn đề này. Tôi sử dụng '_class' như' CLASS_META_KEY' như mongodb. – bekce

1

Theo như tôi có thể nói điều này không làm việc với nhiều loại không thu, hay cụ thể hơn, các tình huống mà các loại bê tông được sử dụng để serialize, và loại giao diện được sử dụng để deserialize. Tức là, nếu bạn có một lớp đơn giản thực hiện một giao diện và bạn sắp xếp lớp bê tông, sau đó chỉ định giao diện để deserialize, bạn sẽ kết thúc trong một tình huống không thể phục hồi.

Trong ví dụ trên, bộ điều hợp loại được đăng ký với giao diện, nhưng khi bạn tuần tự sử dụng lớp bê tông, nó sẽ không được sử dụng, nghĩa là dữ liệu CLASS_META_KEY sẽ không bao giờ được đặt.

Nếu bạn chỉ định bộ điều hợp làm bộ điều hợp phân cấp (do đó yêu cầu gson sử dụng nó cho tất cả các loại trong cấu trúc phân cấp), bạn sẽ kết thúc trong một vòng lặp vô hạn vì bộ nối tiếp sẽ tiếp tục tự gọi.

Bất kỳ ai cũng biết cách tuần tự hóa từ việc triển khai cụ thể giao diện, sau đó deserialize chỉ sử dụng giao diện và InstanceCreator?

Theo mặc định có vẻ như gson sẽ tạo ra trường hợp cụ thể, nhưng không đặt trường của nó.

Issue được đăng ở đây:

http://code.google.com/p/google-gson/issues/detail?id=411&q=interface

0

Bạn phải sử dụng TypeToken lớp từ Google Gson. Bạn sẽ cần tất nhiên có một T lớp tổng quát để làm cho nó hoạt động

Type fooType = new TypeToken<Foo<Bar>>() {}.getType(); 

gson.toJson (foo, fooType);

gson.fromJson(json, fooType); 
Các vấn đề liên quan