FasterXML / jackson-dataformat-xml

Extension for Jackson JSON processor that adds support for serializing POJOs as XML (and deserializing from XML) as an alternative to JSON
Apache License 2.0
567 stars 221 forks source link

Serializing list of objects with custom serializer throws "Current context not Array but Object" #575

Closed dewarim closed 1 year ago

dewarim commented 1 year ago

Using: 2.14.2

When serializing a single object which uses a custom serializer, I get the expected XML result. When the object is part of a list in a wrapper object, I get an exception: com.fasterxml.jackson.databind.JsonMappingException: Current context not Array but Object (through reference chain: com.dewarim.SerializerTest$CoreWrapper["core"])

Small test project: https://github.com/dewarim/jackson-xml-debug2

package com.dewarim;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class SerializerTest {

    private final String xmlContent = "<content><xml>xxx</xml></content>";

    @JsonSerialize(using=CoreSerializer.class)
    public static class Core{
        private final Long   id;
        private final String content;

        public Core(Long id, String content) {
            this.id = id;
            this.content = content;
        }
    }

    public static class CoreSerializer extends JsonSerializer<Core> {

        protected CoreSerializer() {
        }

        @Override
        public void serialize(Core core, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeStartObject();
            jsonGenerator.writeNumberField("id", core.id);
            jsonGenerator.writeObjectFieldStart("content");
            jsonGenerator.writeRaw(core.content);
            jsonGenerator.writeEndObject();
        }
    }

    @Test
    public void serializeTest() throws JsonProcessingException {
        Core core = new Core(1L,xmlContent );
        String value  = new XmlMapper().writeValueAsString(core);
        assertTrue(value.contains(xmlContent));
    }

    public static class CoreWrapper{
        @JacksonXmlElementWrapper(localName = "cores")
        @JacksonXmlProperty(localName = "core")
        List<Core> core= new ArrayList<>();

        public List<Core> getCore() {
            return core;
        }

        public void setCore(List<Core> core) {
            this.core = core;
        }
    }
    @Test
    public void wrappedSerializer() throws JsonProcessingException{

        CoreWrapper wrapper = new CoreWrapper();
        wrapper.setCore(List.of(new Core(2L,xmlContent)));
        // throws: com.fasterxml.jackson.databind.JsonMappingException: Current context not Array but Object (through reference chain: com.dewarim.SerializerTest$CoreWrapper["core"])
        new XmlMapper().writeValueAsString(wrapper);

    }
}
cowtowncoder commented 1 year ago

Ok it may be worth trying to change serializer to not try to write an object but instead

            jsonGenerator.writeFieldName("content");
            jsonGenerator.writeRawValue(core.content);

and see if that helps (note writeRawValue() instead of writeRaw(), to ensure writer state is updated so that a value has been written).

dewarim commented 1 year ago

Thank you, this seems to solve the issue.

cowtowncoder commented 1 year ago

@dewarim Thank you for update & glad things worked out!