Suppress wrapper object when serializing Java object into JSON using Jackson

后端 未结 5 2311
忘掉有多难
忘掉有多难 2021-02-14 23:45

I have a web service that returns a list as JSON. It uses Jackson to map a List of Java POJOs into JSON. The problem is that the JSON representation has a wrapper object around

5条回答
  •  傲寒
    傲寒 (楼主)
    2021-02-15 00:39

    You could write custom serializer:

    public class UnwrappingSerializer extends JsonSerializer
    {
        @Override
        public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonProcessingException
        {
            JavaType type = TypeFactory.type(value.getClass());
            getBeanSerializer(type, provider).serialize(value, new UnwrappingJsonGenerator(jgen), provider);
        }
    
        private synchronized JsonSerializer getBeanSerializer(JavaType type, SerializerProvider provider)
        {
            JsonSerializer result = cache.get(type);
            if (result == null) {
                BasicBeanDescription beanDesc = provider.getConfig().introspect(type);
                result = BeanSerializerFactory.instance.findBeanSerializer(type, provider.getConfig(), beanDesc);
                cache.put(type, result);
            }
            return result;
        }
    
        private Map> cache = new HashMap>();
    
        private static class UnwrappingJsonGenerator extends JsonGeneratorDelegate
        {
            UnwrappingJsonGenerator(JsonGenerator d)
            {
                super(d);
            }
    
            @Override
            public void writeEndObject() throws IOException, JsonGenerationException
            {
                if (depth-- >= yieldDepth) {
                    super.writeEndObject();
                }
            }
    
            @Override
            public void writeFieldName(SerializedString name) throws IOException, JsonGenerationException
            {
                if (depth >= yieldDepth) {
                    super.writeFieldName(name);
                }
            }
    
            @Override
            public void writeFieldName(String name) throws IOException, JsonGenerationException
            {
                if (depth >= yieldDepth) {
                    super.writeFieldName(name);
                }
            }
    
            @Override
            public void writeStartObject() throws IOException, JsonGenerationException
            {
                if (++depth >= yieldDepth) {
                    super.writeStartObject();
                }
            }
    
            private int depth;
            private final int yieldDepth = 2;
        }
    }
    
    
    

    It will ignore outer objects on depth lower than specified (2 by default).

    Then use it as follows:

    public class UnwrappingSerializerTest
    {
        public static class BaseT1
        {
            public List getTest()
            {
                return test;
            }
    
            public void setTest(List test)
            {
                this.test = test;
            }
    
            private List test;
        }
    
        @JsonSerialize(using = UnwrappingSerializer.class)
        public static class T1 extends BaseT1
        {
        }
    
        @JsonSerialize(using = UnwrappingSerializer.class)
        public static class T2
        {
            public BaseT1 getT1()
            {
                return t1;
            }
    
            public void setT1(BaseT1 t1)
            {
                this.t1 = t1;
            }
    
            private BaseT1 t1;
        }
    
        @Test
        public void test() throws IOException
        {
            ObjectMapper om = new ObjectMapper();
            T1 t1 = new T1();
            t1.setTest(Arrays.asList("foo", "bar"));
            assertEquals("[\"foo\",\"bar\"]", om.writeValueAsString(t1));
    
            BaseT1 baseT1 = new BaseT1();
            baseT1.setTest(Arrays.asList("foo", "bar"));
            T2 t2 = new T2();
            t2.setT1(baseT1);
            assertEquals("{\"test\":[\"foo\",\"bar\"]}", om.writeValueAsString(t2));
        }
    }
    

    Notes:

    • It expects only single field wrapper and will generate invalid JSON on something like {{field1: {...}, field2: {...}}
    • If you use custom SerializerFactory you probably will need to pass it to the serializer.
    • It uses separate serializer cache so this also can be an issue.

    提交回复
    热议问题