2015-01-09 14 views
20

Tôi có một lớp trông giống như sauSerialize/Deserialize Map <String, Object> với Jackson

public class MyClass { 
    private String val1; 
    private String val2; 
    private Map<String,Object> context; 
    // Appropriate accessors removed for brevity. 
    ... 
} 

Tôi đang tìm để có thể thực hiện chuyến đi vòng với Jackson từ đối tượng để JSON và ngược lại . Tôi có thể serialize các đối tượng trên tốt và nhận được kết quả như sau:

{ 
    "val1": "foo", 
    "val2": "bar", 
    "context": { 
     "key1": "enumValue1", 
     "key2": "stringValue1", 
     "key3": 3.0 
    } 
} 

Vấn đề tôi đang chạy vào đó là kể từ khi các giá trị trong bản đồ tuần tự không có bất kỳ loại thông tin, họ không deserialized một cách chính xác. Ví dụ, trong ví dụ trên, enumValue1 nên được deserialized như là một giá trị enum nhưng thay vì deserialized như là một String. Tôi đã xem các ví dụ về việc dựa vào những thứ gì trên nhiều thứ khác nhau, nhưng trong trường hợp của tôi, tôi sẽ không biết các loại đó là gì (chúng sẽ là các đối tượng được tạo bởi người dùng mà tôi không biết trước) vì vậy tôi cần phải có thể tuần tự hóa thông tin kiểu với cặp giá trị khóa. Làm thế nào tôi có thể thực hiện điều này với Jackson?

Để lưu nội dung, tôi đang sử dụng phiên bản Jackson 2.4.2. Mã Tôi đang sử dụng để kiểm tra các chuyến đi vòng như sau:

@Test 
@SuppressWarnings("unchecked") 
public void testJsonSerialization() throws Exception { 
    // Get test object to serialize 
    T serializationValue = getSerializationValue(); 
    // Serialize test object 
    String json = mapper.writeValueAsString(serializationValue); 
    // Test that object was serialized as expected 
    assertJson(json); 
    // Deserialize to complete round trip 
    T roundTrip = (T) mapper.readValue(json, serializationValue.getClass()); 
    // Validate that the deserialized object matches the original one 
    assertObject(roundTrip); 
} 

Do đây là một dự án dựa Spring, mapper đã được tạo ra như sau:

@Configuration 
public static class SerializationConfiguration { 

    @Bean 
    public ObjectMapper mapper() { 
     Map<Class<?>, Class<?>> mixins = new HashMap<Class<?>, Class<?>>(); 
     // Add unrelated MixIns 
     .. 

     return new Jackson2ObjectMapperBuilder() 
       .featuresToDisable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS) 
       .dateFormat(new ISO8601DateFormatWithMilliSeconds()) 
       .mixIns(mixins) 
       .build(); 
    } 
} 
+4

Vâng, tất nhiên là một 'Đối tượng' sẽ không có bất kỳ thông tin loại nào; không có cách nào khác ngoài 1. tạo POJO cho biến 'context' instance (ưa thích); hoặc 2. không sử dụng một 'Bản đồ ' nhưng một 'ObjectNode'. Trong mọi trường hợp, đây là một ví dụ chính về một mùi mã. – fge

+0

Biến 'context' đang được sử dụng trong một kiểu tương tự như' HttpSession' (Nó đại diện cho 'ExecutionContext' của Spring Batch là chính xác) vì vậy tôi sẽ không coi nó là một mùi ... đó là cái mà' Map' dùng để làm. Những gì tôi không hiểu là tại sao Jackson không thể thêm một trường cho biết loại. Nếu nó có giá trị, nó có lớp, và do đó có thể xác định loại. Tôi đang thiếu gì? –

+0

Và nó sẽ thêm trường này ở đâu? Bạn chỉ nói với nó để deserialize rằng để một 'Object'. Lợi thế với một 'ObjectNode', ít nhất, là bạn nhận được JSON trực tiếp; do đó bạn có thể truy vấn kiểu của trường với tất cả các 'JsonNode' có thể trợ giúp. – fge

Trả lời

15

Tôi nghĩ rằng cách đơn giản nhất đạt được những gì bạn muốn đang sử dụng:

ObjectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 

Điều này sẽ thêm thông tin loại trong json được tuần tự hóa.

Ở đây bạn là một ví dụ chạy, mà bạn sẽ cần phải thích ứng với mùa xuân:

public class Main { 

    public enum MyEnum { 
     enumValue1 
    } 

    public static void main(String[] args) throws IOException { 
     ObjectMapper mapper = new ObjectMapper(); 

     MyClass obj = new MyClass(); 
     obj.setContext(new HashMap<String, Object>()); 

     obj.setVal1("foo"); 
     obj.setVal2("var"); 
     obj.getContext().put("key1", "stringValue1"); 
     obj.getContext().put("key2", MyEnum.enumValue1); 
     obj.getContext().put("key3", 3.0); 

     mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 
     String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); 

     System.out.println(json); 

     MyClass readValue = mapper.readValue(json, MyClass.class); 
     //Check the enum value was correctly deserialized 
     Assert.assertEquals(readValue.getContext().get("key2"), MyEnum.enumValue1); 
    } 

} 

Đối tượng sẽ được tuần tự vào một cái gì đó tương tự như:

[ "so_27871226.MyClass", { 
    "val1" : "foo", 
    "val2" : "var", 
    "context" : [ "java.util.HashMap", { 
    "key3" : 3.0, 
    "key2" : [ "so_27871226.Main$MyEnum", "enumValue1" ], 
    "key1" : "stringValue1" 
    } ] 
} ] 

Và sẽ được deserialized lại một cách chính xác và xác nhận sẽ vượt qua.

Đường nhánh có nhiều cách để thực hiện việc này, vui lòng xem https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization để biết thêm thông tin.

Tôi hy vọng điều đó sẽ hữu ích.

+0

Đó chính xác là những gì tôi đang tìm kiếm. Một câu hỏi nhỏ. Đây là mức toàn cầu. Có cách nào để xác định điều này ở một cấp trường (Chỉ có một trường mà tôi có vấn đề này vì vậy nó sẽ là tốt đẹp để không xả rác JSON với các loại ở khắp mọi nơi). Không phải là một vấn đề lớn, nhưng không thể yêu cầu. –

+1

Cảm ơn bạn, bạn đã kết thúc giờ đau đớn của tôi – James

+1

Tôi tin rằng việc bật tùy chọn này có thể ảnh hưởng bất ngờ đến JSON được tạo ra bởi việc tuần tự hóa đối tượng của bạn. Vì vậy, tôi sẽ không hỗ trợ cho phép một tùy chọn toàn cầu cho trường hợp này. Điều tôi mong đợi là khi tôi thêm một thể hiện đa hình vào một Bản đồ/Bộ sưu tập, các tùy chọn tuần tự kiểu không bị bỏ qua. Đáng ngạc nhiên là, khi đối tượng polymorphic tồn tại dưới một đối tượng container/aggregation, các tùy chọn serialization được áp dụng như mong đợi. –

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