使用 Jackson 将列表序列化为 xml 而不带注释?

Serialize List to xml with Jackson without Annotation?

提问人:NotX 提问时间:6/27/2018 最后编辑:SonhnLabNotX 更新时间:11/18/2019 访问量:866

问:

我正在寻找一种方法来(反)序列化项目,而无需在 Jackson 中使用注释。这可能吗?到目前为止,我正在做的是尝试将 -tag 替换为一个标签,告诉项目的类,但无济于事。即使这有效,我不确定杰克逊是否会提供一种处理此标签信息的方法。List<item>

为了更好地说明我的目标,这里有一个示例:

public class JacksonTest {

    private static class ListElement {
        private boolean value;
        // getters, setters, constructors omitted
    }

    @Test
    public void testDeSerialization() throws Exception {
        final List<ListElement> existing = Arrays.asList(new ListElement(true));
        final ObjectMapper mapper = new XmlMapper();
        final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
        final String listString = mapper.writerFor(listJavaType).writeValueAsString(existing);
        System.out.println(listString);
        // "<List><item><value>true</value></item></List>"
    }

}

因此,结果是 ,而我希望将 -tag 替换为(限定的)类名或提供 -属性。 当然,如果 Jackson 中没有办法处理这个类名,即使这样也无济于事。<List><item><value>true</value></item></List><item>type

我在这里是否已经走到了死胡同,还是有路可走?

Java XML 泛型 集合 Jackson

评论


答:

2赞 Evertude 6/27/2018 #1

您可以定义自己的 JsonSerializer(也用于 XML)并将其添加到 JacksonXmlModule。

ToXmlGenerator 具有 setNextName 函数,可用于重写默认项名称

private class MyListSerializer extends JsonSerializer<List> {
    @Override
    public void serialize(List list, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        for (Object obj : list) {
            if (jsonGenerator instanceof ToXmlGenerator) {
                ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
                String className = obj.getClass().getSimpleName();
                xmlGenerator.setNextName(new QName(className));
            }
            jsonGenerator.writeObject(obj);
            // this is overridden at the next iteration
            // and ignored at the last
            jsonGenerator.writeFieldName("dummy");
        }
    }

    @Override
    public Class<List> handledType() {
        return List.class;
    }
}

@Test
public void testDeSerialization() throws Exception {
    final List<ListElement> existing = Arrays.asList(new ListElement(true));
    JacksonXmlModule module = new JacksonXmlModule();
    module.addSerializer(new MyListSerializer());
    final ObjectMapper mapper = new XmlMapper(module);
    final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
    final ObjectWriter writer = mapper.writerFor(listJavaType);
    final String listString = writer.writeValueAsString(existing);
    System.out.println(listString);
    // "<List><ListElement><value>true</value></ListElement></List>"
}

评论

0赞 NotX 6/28/2018
嗯,这看起来是一个可行的方法!到目前为止谢谢你!但它不适用于具有多个元素的 List,因为第二次调用一次。JsonGenerationException - Can not start an object, expecting field namewriteObject(obj)
0赞 NotX 6/28/2018 #2

好的,在对 Evertude 的提议进行了一些修补和调试之后,我想出了一个解决方案。我对序列化部分不太满意,老实说,我不知道为什么我应该这样做。调试时,我注意到需要调用一次,但对下一次调用没有任何影响,因此我不得不在那里实现一个开关并直接设置循环中下一项的字段名称。XmlGenerator::setNextName

如果有人知道我做错了什么,我会很高兴,但至少我的尝试现在有效:

@Test
public void testDeSerialization() throws Exception {
    final List<ListElement> existing = Arrays.asList(new ListElement(true), new ListElement(false));
    JacksonXmlModule module = new JacksonXmlModule();
    module.addSerializer(new MyListSerializer());
    final ObjectMapper mapper = new XmlMapper(module);
    final JavaType listJavaType = mapper.getTypeFactory().constructCollectionType(List.class, ListElement.class);
    final ObjectWriter writer = mapper.writerFor(listJavaType);
    final String listString = writer.writeValueAsString(existing);
    module.addDeserializer(List.class, new MyListDeserializer());
    List<ListElement> deserialized = mapper.readValue(listString, List.class);
    assertEquals(existing, deserialized); // provided there're proper hash() and equals() methods
}

private class MyListSerializer extends JsonSerializer<List> {

    @Override
    public void serialize(List list, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        boolean done = false;
        for (Object obj : list) {
            if (jsonGenerator instanceof ToXmlGenerator) {
                ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
                String className = obj.getClass().getSimpleName();
                // weird switch
                if (!done) xmlGenerator.setNextName(new QName(className));
                else jsonGenerator.writeFieldName(className);
                done = true;
            }
            jsonGenerator.writeObject(obj);
        }
    }

    @Override
    public Class<List> handledType() {
        return List.class;
    }
}

private class MyListDeserializer extends JsonDeserializer<List> {

    @Override
    public List deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        List<Object> items = new ArrayList<>();
        JsonToken nextToken;
        while ((nextToken = p.nextToken()) != JsonToken.END_OBJECT) {
            String currentName = p.currentName();
            try {
                String className = "my.test.project.JacksonCustomSerializer$" + currentName;
                Class<?> loadClass = getClass().getClassLoader().loadClass(className);
                p.nextToken();
                items.add(p.readValueAs(loadClass));
            } catch (ClassNotFoundException e) {
                // some handling
            }
        }
        return items;
    }

    @Override
    public Class<List> handledType() {
        return List.class;
    }
}