简体   繁体   English

使用没有注释的 Jackson 将列表序列化为 xml?

[英]Serialize List to xml with Jackson without Annotation?

I'm looking for a way to (de-)serialize a List of items without using Annotations in Jackson.我正在寻找一种在不使用 Jackson 注释的情况下(反)序列化项目List的方法。 Is this possible?这可能吗? What I'm doing up to now is trying to replace the <item> -tag with a tag telling about the item's class, but no avail.到目前为止,我正在做的是尝试将<item> -tag 替换为一个说明项目类别的标签,但无济于事。 And even if this worked, I'm not sure whether Jackson would offer a way to process this tag information.即使这行得通,我也不确定 Jackson 是否会提供处理此标签信息的方法。

To give a better of what I'm aiming at, here's a sample:为了更好地了解我的目标,这里有一个示例:

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>"
    }

}

So, the result is <List><item><value>true</value></item></List> , while I want the <item> -tag to be replaced with the (qualified) class name or offering a type -attribute.因此,结果是<List><item><value>true</value></item></List> ,而我希望将<item> -tag 替换为(限定的)类名或提供一个type属性。 Of course, even this would not help if there's no way in Jackson to process this class name.当然,如果 Jackson 没有办法处理这个类名,即使这样也无济于事。

Do I have reached a dead end here or is there a way to go?我在这里已经走到了死胡同还是有路可走?

You can define your own JsonSerializer (also used for XML) and add it to a JacksonXmlModule.您可以定义自己的 JsonSerializer(也用于 XML)并将其添加到 JacksonXmlModule。

ToXmlGenerator has a setNextName function that allows you to override the default item name 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>"
}

Okay, after some tinkering and debugging with Evertude's proposal I've figured out a solution.好的,在对 Evertude 的提议进行一些修补和调试之后,我找到了一个解决方案。 I'm not really happy with the serialization part and honestly I don't know why I was supposed to do it this way.我对序列化部分不太满意,老实说我不知道​​为什么我应该这样做。 When debugging I've noticed that XmlGenerator::setNextName is required to be called once but does not have any effect on the next call, so I had to implement a switch there and set the field name for the next item in the loop directly.调试时我注意到XmlGenerator::setNextName需要被调用一次,但对下一次调用没有任何影响,所以我不得不在那里实现一个开关并直接为循环中的下一个项目设置字段名称。

I'ld be glad if somebody has an idea what I'm doing wrong, but at least my attempt is working for now:如果有人知道我做错了什么,我会很高兴,但至少我现在的尝试是有效的:

@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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM