2011-12-16 47 views
11

Ok vì vậy tôi đã chỉnh sửa câu hỏi vì nó không đủ rõ ràng.GSON: tùy chỉnh đối tượng deserialization

Chỉnh sửa 2: đã cập nhật tệp JSON.

Tôi đang sử dụng GSON trong ứng dụng Android và tôi cần phải phân tích cú pháp các tệp JSON, xuất phát từ máy chủ và có quá phức tạp. Tôi không muốn có cấu trúc đối tượng quá nặng, vì vậy tôi muốn đơn giản hóa các nội dung: vì vậy cấu trúc của đối tượng của tôi sẽ không phải là cấu trúc của tệp JSON.

Ví dụ, nếu trong JSON Tôi có điều này:

{ 
    "object1":{ 
     "attribute1" : "test1", 
     "attribute40" : "test40", 
     "user":{ 
      "id":1, 
      "name":"foo" 
     } 
     ,"example":{ 
      "total":10, 
      "list":[ 
      { 
       "tag":"tag1", 
       "name":"object name 1", 
       "pos":1 
      }, 
      { 
       "tag":"tag10", 
       "name":"object name 10", 
       "pos":10 
      } 
     ] 
     } 
    } 
    "object2":{ 
     "attribute1":"test..." 
    } 
} 

Tôi không muốn giữ lại trong cơ cấu của tôi hiện tại đối tượng, một đối tượng Example, có chứa một ArrayList và một "tổng" int . Nhưng tôi muốn giữ chỉ một Chuỗi đơn giản với giá trị "object name 1;object name 2;...".

Hơn nữa, tôi chỉ muốn lưu trữ Id người dùng, không phải là Người dùng hoàn chỉnh, vì tôi đã có người dùng hoàn chỉnh được lưu trữ ở nơi khác, với một cuộc gọi API máy chủ khác.

Vì vậy, lớp lớp học của tôi sẽ là một cái gì đó như:

class Foo{ 
    int userId; 
    String example; //"object name 1;object name 2;..." 
    ... 
} 

Vì vậy, tôi cho rằng chúng ta có thể đạt được điều này với một deserializer tùy chỉnh, nhưng tôi không thấy như thế nào. Tôi muốn nếu có thể để giảm thiểu bộ nhớ, vì vậy tôi không nghĩ rằng có một ví dụ đối tượng đầy đủ, và sau đó sử dụng nó để xây dựng String example của tôi là một cách chính xác.

Trong trường hợp xấu nhất, nếu nó quá phức tạp, tôi muốn có thể lưu trữ ít nhất chỉ danh sách các mục thẻ khi tôi phân tích đối tượng ví dụ: vì vậy tôi cần một trình gỡ rối tùy chỉnh để loại bỏ phần int total .

Vì vậy, tôi sẽ có:

class Foo{ 
    int userId; 
    ArrayList<Tag> example; 
    ... 
} 
+0

Theo yêu cầu đặc biệt của bạn, GSON bị quá tải. Chỉ cần chuyển chuỗi JSON của bạn vào hàm tạo của mô hình miền của bạn, sau đó sử dụng phân tách thao tác chuỗi đơn giản/trích xuất trường bắt buộc. – yorkw

+0

Vâng, chắc chắn nó có thể là một giải pháp, nhưng tôi có rất nhiều tệp JSON với hơn 30 trường mỗi lần và cấu trúc có thể phát triển trong tương lai. Vì vậy, tôi chắc chắn sẽ thích sử dụng một lib như GSON để giảm thiểu công việc và bảo trì. Đặc biệt là nếu tôi đã sử dụng nó trên một số tệp JSON. – Chayy

+0

"cấu trúc có thể phát triển trong tương lai", từ quan điểm OO, bạn nên tạo mô hình đối tượng miền hoàn chỉnh ngay bây giờ, mặc dù bạn không sử dụng tất cả các thuộc tính của nó. – yorkw

Trả lời

19

Tôi đã thông qua câu trả lời để trình bày giải pháp đầy đủ được thiết kế trong trò chuyện và phù hợp với chuỗi JSON đã thay đổi. Mã này giả định rằng chuỗi json nắm giữ chính xác JSON (cập nhật) từ câu hỏi. Yêu cầu là để lấp đầy lớp sau (setter và toString ommitted):

class Object1 
{ 
    private String attribute1; 
    private String attribute40; 
    private int userId; 
    private String nameList; 
} 

GSON hỗ trợ (như REST-libs khác nhất) ba chế độ:

  • GSON_DOM
    Đọc toàn bộ JSON qua JsonParser.parse() và xây dựng một cây DOM trong bộ nhớ (truy cập mô hình đối tượng). Do đó, giải pháp này tốt cho các tệp JSON nhỏ.
  • GSON_STREAM
    Chỉ đọc các khối của JSON qua JsonReader. Mã là phức tạp hơn, nhưng nó phù hợp cho các tệp JSON lớn. Kể từ Android 3.0 Tổ ong, bộ phân tích cú pháp truyền trực tuyến của GSON được bao gồm dưới dạng android.util.JsonReader.
  • GSON_BIND
    Gắn kết trực tiếp với lớp học qua phản chiếu, giảm thiểu mã đáng kể. GSON cho phép chế độ hỗn hợp, có nghĩa là kết hợp GSON_DOM và GSON_BIND hoặc GSON_STREAM và GSON_BIND mà câu trả lời này sẽ hiển thị.

Để lấp đầy lớp Object1 qua GSON_DOM và GSON_BIND việc thực hiện trông giống như:

private static void deserializeViaObjectAccess(final String json) 
{ 
    Gson gson = new Gson(); 

    // Read the whole JSON into meomory via GSON_DOM 
    JsonParser parser = new JsonParser(); 
    JsonObject object1 = parser.parse(json).getAsJsonObject().getAsJsonObject("object1"); 

    // map the Object1 class via GSON_BIND 
    // (bind common attributes which exist in JSON and as properties in the class) 
    // mapper acts as factory 
    Object1 result = gson.fromJson(object1, Object1.class); 

    // manually read the attribute from the user object 
    int userId = object1.getAsJsonObject("user").getAsJsonPrimitive("id").getAsInt(); 
    result.setUserId(userId); 

    // manually read the attributes from the example object 
    String names = ""; 
    JsonArray list = object1.getAsJsonObject("example").getAsJsonArray("list"); 
    for (int i = 0; i < list.size(); ++i) 
    { 
     JsonObject entry = list.get(i).getAsJsonObject(); 
     String name = entry.getAsJsonPrimitive("name").getAsString(); 

     names = i == 0 ? name : names + "; " + name; 
    } 
    result.setNameList(names); 

    // Output the result 
    log.debug(result.toString()); 
} 

Để lấp đầy lớp Object1 qua GSON_STREAM và GSON_BIND việc thực hiện trông giống như:

Tại thời điểm này , điều này chỉ có thể xảy ra khi một nút được tải hoàn toàn qua GSON_BIND hoặc GSON_STREAM. Ví dụ này cần rằng một nút chính nó nên được chia nhỏ. Đây chỉ là có thể với phiên bản sắp tới 2.2. Tôi sẽ chuyển mã sau này khi có GSON 2.2. *

+0

2.2.4 là phiên bản mới nhất hiện có, vui lòng cho chúng tôi biết cách sử dụng GSON_BIND –

1

De-Serialize ví dụ JSON thành đối tượng Ví dụ đầy đủ, sử dụng các thuộc tính tên của đối tượng Ví dụ để xây dựng một chuỗi những điều bạn muốn, quên đi những ví dụ vật.

Tôi không thực sự hiểu câu hỏi thứ hai hoàn toàn, nhưng nếu bạn có một đối tượng Test1 đầy đủ sẽ tất cả các trường/thuộc tính thì bạn có thể tạo đối tượng Test2 lấy trường từ Test1 mà nó muốn. Ví dụ: đối tượng Test2 của bạn có thể chấp nhận Test1 như một tham số trong hàm tạo của nó và chỉ lấy các thuộc tính mà nó cần bỏ qua phần còn lại.

+0

Ok vì vậy câu hỏi của tôi là nhiều hơn: làm thế nào tôi có thể làm điều đó mà không cần phải lưu trữ các đối tượng đầy đủ trong cấu trúc của tôi. Ví dụ, tôi có thể thực hiện 'gson.fromJson()', nó sẽ nhận tất cả các trường trừ chuỗi nối của tôi, và sau đó thực hiện kết nối theo cách thủ công. Cùng một điều để giữ chỉ một id. Nhưng tôi không muốn có những lĩnh vực vô dụng trong bộ nhớ, tôi muốn làm điều này trong quá trình serialization. Đối với câu hỏi thứ hai, không có hai đối tượng 'Test1' và' Test2' là độc lập: đối tượng 'Test2' có thể được tạo trước đối tượng' Test1'. – Chayy

1

Trong khi bạn đang phát trực tuyến trong Jsons qua http, bạn có thể chỉ cần hủy văn bản và chỉ lưu trữ các đối tượng tùy chỉnh của mình. Trong trường hợp này, bạn sẽ liên tục loại bỏ các thông tin không cần thiết.

While stream not empty 

    Read the next block into a new string 

    Deserialize the string to the your object 

    Store the object in a myArrayList 

Lưu ý: đọc toàn bộ JSON và tiêu thụ toàn bộ, rất cần thiết nếu bạn muốn ứng dụng của mình mạnh mẽ. Trừ khi bạn muốn đọc JSON như là một luồng ký tự thô (tôi nghi ngờ, trừ khi JSON của bạn thực sự là, cực kỳ lớn, rằng phím tắt này là cần thiết).

Neverthelss, đọc luồng đầu vào mà không áp đặt và yêu cầu tạo thành tốt JSON, có thể được thực hiện mà không cần phải viết và cấu trúc dữ liệu không cần thiết vào bộ nhớ. Điều này có thể hoạt động nếu bạn chỉ muốn một tập con nhỏ của dữ liệu- I.e.Bạn chỉ muốn tên của mọi người hoặc các url trong JSON. Nhưng nó sẽ phá vỡ nếu bạn muốn cấu trúc dữ liệu phức tạp hơn. Tuy nhiên:

// ví dụ phân tích url từ dòng JSON mà không cần lưu trữ toàn bộ cấu trúc dữ liệu

While input stream is not empty 

    String x = nextLine 

    If x contains "http" 

     myArrayList.add(parseUrl(x) 

suy nghĩ cuối cùng:

Bur cuối cùng, Jsons REST của yêu cầu không giống SQL- bạn không thể generucally và tùy tiện Bỏ qua một số trường nhất định. Có lẽ, nếu bạn thực sự muốn các Jsons trọng lượng nhẹ hơn, cách tiếp cận tự nhiên hơn là nói hoặc kiểm tra xem nhà cung cấp dịch vụ RESt của bạn có thể đơn giản mở rộng các loại tham số yêu cầu RESt để phù hợp với trường hợp sử dụng của bạn hay không.

+0

Ok thx cho câu trả lời này, đó là một cách tiếp cận mà tôi chưa thử. Nhưng khi truy cập mô hình đối tượng mà tôi đã thử, với kỹ thuật này, bạn cần phải kiểm tra thủ công từng trường một lần nữa tên trường của bạn. Tất nhiên với điều này tôi có thể loại bỏ các thông tin không cần thiết, nhưng nó cũng cần nhiều công việc hơn, và một bảo trì khó khăn hơn. Ngay cả khi phương pháp của bạn tốt hơn về hiệu suất hơn so với phương pháp đầu tiên của tôi, nó không hoàn toàn phù hợp với nhu cầu của tôi. Tôi đang tìm kiếm một cái gì đó tự động hơn, nơi mà tôi có thể thực hiện công việc cụ thể của mình trên một đối tượng và để các trường khác được trình phân tích cú pháp quản lý. – Chayy

2

Một tùy chọn sẽ là phân tích chuỗi JSON bằng trình phân tích cú pháp được xây dựng bên trong Gson như chi tiết here. Bạn sẽ làm một cái gì đó như thế này:

com.google.gson.JsonParser parser = new JsonParser(); 
JsonObject object = parser.parse(data).getAsJsonObject(); 
JsonObject example = object.getAsJsonObject("example"); 
JsonArray list = example.getAsJsonArray("list"); 

JsonObjectJsonArray là một phần của Gson riêng của mình.

Sau khi sử dụng, bạn có thể sử dụng các hàm như getAsInt để phân tích các trường riêng lẻ và tạo và trả về bất kỳ đối tượng nào bạn muốn.

Sửa 1

Nó có vẻ như là bạn có thể sử dụng gson.fromJson trên một lớp tùy chỉnh quá và không chỉ các loại Java generic như được đưa ra trong example này. Vì vậy, bạn phải phân tích chuỗi JSON của bạn bằng cách sử dụng phân tích cú pháp và gọi fromJson trên một trong các đối tượng bên trong hoặc mảng.

+0

Ok, đó là cách tiếp cận đầu tiên tôi đã làm, với các lớp học org.json kinh điển. Nhược điểm của kỹ thuật này là tôi phải thực hiện việc kết hợp giữa các trường đối tượng của tôi và các trường JSON. Đó là rất nhiều công việc và maitnenance khi tôi có hơn 30-40 lĩnh vực. – Chayy

+0

Có vẻ như bạn cũng có thể sử dụng fromJson ở một trong các lớp bên trong. Điều này có thể phù hợp với nhu cầu của bạn. – Abhinav

0

Tôi sẽ đề xuất sử dụng thư viện Jackson. Nó được cung cấp với giấy phép Apache, vì vậy bạn có thể sử dụng nó để sử dụng thương mại miễn phí. Trong tutorial tìm kiếm vốn "Ví dụ về API truyền trực tuyến". Nó rất dễ dàng và bạn kiểm soát toàn bộ quá trình phát trực tiếp. Vì vậy, bạn có thể lấy những gì bạn muốn và bỏ qua tất cả những thứ khác. Thư viện Jackson được chia thành một số lọ. Các jar hỗ trợ streaming API, là nhỏ nhất và không sử dụng bất kỳ khác. Đó là câu trả lời, tôi nghĩ vậy.

Jackson có thể cung cấp nhiều hơn nữa. Trong this article bạn có thể tìm thấy phương pháp để đọc tệp JSON ở cấp cao hơn, dưới dạng phần tử, nhưng với cài đặt PREVIOUSLY những đối tượng bạn cần và những gì không cần. Vì vậy, bạn có thể có kết quả phân tích cú pháp ngay lập tức chỉ các yếu tố bạn cần.

+0

Có vẻ như thư viện GSON cũng cung cấp chế độ phát trực tuyến, ngay cả khi nó có vẻ kém hiệu quả hơn Jackson. Và dù sao tôi cũng nói với những người khác, nó không phù hợp với nhu cầu của tôi. – Chayy

+0

Bạn có nghĩa là GSON, không phải JSON, phải không? Dù sao, bạn muốn có đối tượng JSON được xây dựng lại, nhưng chỉ với các trường bắt buộc? Có thể, bạn nên chỉnh sửa câu hỏi của mình. Câu hỏi không nên bị trập giữa các bình luận. – Gangnus

+0

Bạn có thể tạo lại đối tượng JSON, nhưng chỉ với các trường bắt buộc - Đó chỉ là những gì tôi đang nói trong đoạn thứ hai của câu trả lời của tôi. – Gangnus

Các vấn đề liên quan