Jackson: XML to Map with List deserialization

前端 未结 3 679
暖寄归人
暖寄归人 2020-12-15 05:52

Is there a way to deserialize the following xml into Map holding List of items using Jackson?


    12345678
    <         


        
3条回答
  •  挽巷
    挽巷 (楼主)
    2020-12-15 06:19

    This is a known jackson-dataformat-xml bug filed under issue 205. In a nutshell, duplicated elements in the XML get swallowed by the current UntypedObjectDeserializer implementation. Fortunately, the author (João Paulo Varandas) of the report also provided a temporary fix in the form a custom UntypedObjectDeserializer implementation. Below I share my interpretation of the fix:

    import com.fasterxml.jackson.core.JsonParser;
    import com.fasterxml.jackson.core.JsonToken;
    import com.fasterxml.jackson.databind.DeserializationContext;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;
    import com.fasterxml.jackson.databind.module.SimpleModule;
    import com.fasterxml.jackson.dataformat.xml.XmlMapper;
    
    import javax.annotation.Nullable;
    import java.io.IOException;
    import java.util.*;
    
    public enum JacksonDataformatXmlIssue205Fix {;
    
        public static void main(String[] args) throws IOException {
            String xml = "\n" +
                    "\n" +
                    "    1\n" +
                    "    2\n" +
                    "    3\n" +
                    "";
            SimpleModule module = new SimpleModule().addDeserializer(Object.class, Issue205FixedUntypedObjectDeserializer.getInstance());
            XmlMapper xmlMapper = (XmlMapper) new XmlMapper().registerModule(module);
            Object object = xmlMapper.readValue(xml, Object.class);
            System.out.println(object);     // {item=[{id=1}, {id=2}, {id=3}]}
        }
    
        @SuppressWarnings({ "deprecation", "serial" })
        public static class Issue205FixedUntypedObjectDeserializer extends UntypedObjectDeserializer {
    
            private static final Issue205FixedUntypedObjectDeserializer INSTANCE = new Issue205FixedUntypedObjectDeserializer();
    
            private Issue205FixedUntypedObjectDeserializer() {}
    
            public static Issue205FixedUntypedObjectDeserializer getInstance() {
                return INSTANCE;
            }
    
            @Override
            @SuppressWarnings({ "unchecked", "rawtypes" })
            protected Object mapObject(JsonParser parser, DeserializationContext context) throws IOException {
    
                // Read the first key.
                @Nullable String firstKey;
                JsonToken token = parser.getCurrentToken();
                if (token == JsonToken.START_OBJECT) {
                    firstKey = parser.nextFieldName();
                } else if (token == JsonToken.FIELD_NAME) {
                    firstKey = parser.getCurrentName();
                } else {
                    if (token != JsonToken.END_OBJECT) {
                        throw context.mappingException(handledType(), parser.getCurrentToken());
                    }
                    return Collections.emptyMap();
                }
    
                // Populate entries.
                Map valueByKey = new LinkedHashMap<>();
                String nextKey = firstKey;
                do {
    
                    // Read the next value.
                    parser.nextToken();
                    Object nextValue = deserialize(parser, context);
    
                    // Key conflict? Combine existing and current entries into a list.
                    if (valueByKey.containsKey(nextKey)) {
                        Object existingValue = valueByKey.get(nextKey);
                        if (existingValue instanceof List) {
                            List values = (List) existingValue;
                            values.add(nextValue);
                        } else {
                            List values = new ArrayList<>();
                            values.add(existingValue);
                            values.add(nextValue);
                            valueByKey.put(nextKey, values);
                        }
                    }
    
                    // New key? Put into the map.
                    else {
                        valueByKey.put(nextKey, nextValue);
                    }
    
                } while ((nextKey = parser.nextFieldName()) != null);
    
                // Ship back the collected entries.
                return valueByKey;
    
            }
    
        }
    
    }
    
        

    提交回复
    热议问题