使用没有注释的杰克逊将列表序列化为xml?

时间:2018-06-27 12:30:29

标签: java xml generics collections jackson

我正在寻找一种方法,以在List项中进行反序列化,而无需在Jackson中使用注释。这可能吗?我到目前为止正在做的是尝试用一个<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>"
    }

}

因此,结果是<List><item><value>true</value></item></List>,而我希望将<item>标记替换为(限定的)类名称或提供type属性。 当然,如果杰克逊无法处理此类名称,即使这样做也无济于事。

我在这里已经走到了尽头还是有路要走?

2 个答案:

答案 0 :(得分: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>"
}

答案 1 :(得分:0)

好的,在对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() // TODO put qualifier in ctxt?
                        .loadClass(className);
                p.nextToken();
                items.add(p.readValueAs(loadClass));
            } catch (ClassNotFoundException e) {
                // some handling
            }
        }
        return items;
    }

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