2015-10-24 16 views
5

Tôi nhận JSON từ API và phản hồi có thể là một trong 30 loại. Mỗi loại có một tập hợp các trường duy nhất, nhưng tất cả các câu trả lời đều có một trường type cho biết loại đó là gì.JSON giải mã có điều kiện dựa trên một trường trong JSON

Cách tiếp cận của tôi là sử dụng serde. Tôi tạo một cấu trúc cho từng loại phản hồi và làm cho chúng có thể giải mã được. Một khi tôi đã làm điều đó làm thế nào để tôi chọn cấu trúc nào nên được sử dụng cho một tin nhắn mới nhận được?

Hiện tại, tôi đã tạo cấu trúc khác TypeStruct chỉ với một trường duy nhất cho type. Tôi giải mã JSON thành một TypeStruct, sau đó chọn cấu trúc thích hợp cho thông điệp nhận được, dựa trên giá trị kiểu và giải mã thông điệp một lần nữa.

Tôi muốn loại bỏ bản sao giải mã này.

+0

Giải pháp hiện tại của bạn là những gì tôi sẽ làm. Bạn nghĩ điều gì sẽ tốt hơn hoặc hiệu quả hơn trong việc tránh "sự sao chép mã hóa"? – Shepmaster

+0

Tôi đang đến từ python, vì vậy tôi không cảm thấy thoải mái với các ngôn ngữ được đánh máy. Nhưng nếu bạn nói, cách tiếp cận của tôi là ok, tôi sẽ gắn bó với nó. – eyeinthebrick

+0

Chỉ là ý kiến ​​của tôi, nhưng điều này nghe có vẻ giống như một ví dụ tốt về sự lo lắng sớm về tối ưu hóa. Nhận mã của bạn để chạy và xem bạn có cho là đủ nhanh không. Nếu không, hãy cấu hình mã của bạn trước khi thay đổi nó - có lẽ giải mã JSON thậm chí không phải là vấn đề. – llogiq

Trả lời

6

Bạn có thể sử dụng tính năng deserialization enum hiện tại. Tôi sẽ đưa cho một từng bước dụ để deserialize định dạng của bạn để enum sau:

#[derive(Debug, PartialEq, Eq, Deserialize)] 
enum MyType { 
    A {gar:()}, 
    B {test: i32}, 
    C {blub: String}, 
} 
  1. Bắt đầu với một chuỗi ví dụ json:

    let json = r#"{"type": "B", "test": 42}"#; 
    
  2. biến nó thành một Value enum

    let mut json: serde_json::Value = serde_json::from_str(json).unwrap(); 
    
  3. Trích xuất type trường

    let type_ = { 
        let obj = json.as_object_mut().expect("object"); 
        let type_ = obj.remove("type").expect("`type` field"); 
        if let serde_json::Value::String(s) = type_ { 
         s 
        } else { 
         panic!("type field not a string"); 
        } 
    }; 
    
  4. Tạo "thích hợp" enum json. Một struct với một lĩnh vực duy nhất mà tên của lĩnh vực này là biến thể enum và giá trị của lĩnh vực này là giá trị biến

    let mut enum_obj = std::collections::BTreeMap::new(); 
    enum_obj.insert(type_, json); 
    let json = serde_json::Value::Object(enum_obj); 
    
  5. Sử dụng deserializer json tạo để biến json thành một giá trị của enum của bạn

    let obj: MyType = serde_json::from_value(json).unwrap(); 
    
+1

Bất kỳ số hoặc cảm giác ruột nào về hiệu quả của điều này so với giải pháp "giải mã hai lần" được nêu trong câu hỏi? – Shepmaster

+3

nó là một giải pháp DRY. Bằng cách này, bạn không cần xử lý bất kỳ loại nào ở hai vị trí. Bạn chỉ cần khai báo enum. Không bao giờ khớp với tất cả các loại có thể hoặc bất kỳ thứ gì. Speedwise này có lẽ còn tồi tệ hơn so với giải pháp được cung cấp trong câu hỏi (ngoại trừ nếu có rất nhiều trường 'String' và không có trường struct/enum, thì nó có thể nhanh hơn giải pháp trong câu hỏi). Thật khó để biết mà không nhìn thấy mã chính xác. Các giải pháp chung nhanh hơn đòi hỏi kiến ​​thức chính xác về thứ tự trường trong chuỗi json và/hoặc hack trên serde –

+0

Cảm ơn bạn, tôi nghi ngờ rằng, một cái gì đó như bạn đề xuất là có thể. Tôi có thể sử dụng cùng một kỹ thuật với tuần tự hóa gỉ không? – eyeinthebrick

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