Подавить объект-оболочку при сериализации Java-объекта в JSON с использованием Jackson - PullRequest
8 голосов
/ 22 января 2011

У меня есть веб-сервис, который возвращает список в формате JSON. Он использует Джексона для отображения Списка Java POJO в JSON. Проблема в том, что представление JSON имеет объект-оболочку вокруг массива, а я просто хочу массив. Т.е. я получаю это:

{"optionDtoList":[{...}, ..., {...}]}

когда я действительно хочу это:

[{...}, ..., {...}]

Я сериализую список Java напрямую; Я не обертываю List объектом-оберткой и не сериализую объект-обертку. Это Джексон, кажется, добавляет объект обертки JavaScript.

Я предполагаю, что есть некоторая аннотация, которую я могу использовать в POJO для подавления объекта-оболочки, но я его не вижу.

Ограничения на решение

Я бы хотел исправить это на стороне службы, а не снимать оболочку с клиента. Клиент - это виджет jQuery UI (виджет автозаполнения, хотя это и не важно), который ожидает простой массив, и я не хочу изменять сам виджет.

Что я пробовал

  • Я попытался заменить Список Java POJO массивом Java POJO, и результат тот же.
  • Я пытался @JsonTypeInfo(use = Id.NONE) думать, что это может подавить оболочку, но это не так.

Ответы [ 5 ]

5 голосов
/ 15 февраля 2011

У меня та же проблема, что и у вас.

После добавления @ResponseBody перед моим списком в моем объявлении метода проблема была решена.

например:

public @ResponseBody List<MyObject> getObject

5 голосов
/ 25 января 2011

В тестовом режиме при запуске:

org.codehaus.jackson.map.ObjectMapper mapper = new org.codehaus.jackson.map.ObjectMapper();
String json = mapper.writeValueAsString( Arrays.asList("one","two","three","four","five") );
System.out.println(json);

возвращает:

["one","two","three","four","five"]

какое поведение вы ожидаете, верно?

Я мог видеть, чтокогда я возвращаю этот список через контроллер Spring и позволяю MappingJacksonJsonView обрабатывать преобразование списка в json, тогда да, вокруг него есть обертка, которая говорит мне, что MappingJacksonJsonView - это тот, который добавляет обертку.Тогда одним из решений будет явный возврат json из вашего контроллера, скажем:

    @RequestMapping(value = "/listnowrapper")
public @ResponseBody String listNoWrapper() throws Exception{       
    ObjectMapper mapper = new ObjectMapper();
    return mapper.writeValueAsString(Arrays.asList("one","two","three","four","five")); 
}
1 голос
/ 24 января 2011

Вы можете написать собственный сериализатор:

public class UnwrappingSerializer extends JsonSerializer<Object>
{
    @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<Object> getBeanSerializer(JavaType type, SerializerProvider provider)
    {
        JsonSerializer<Object> 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<JavaType, JsonSerializer<Object>> cache = new HashMap<JavaType, JsonSerializer<Object>>();

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

Он будет игнорировать внешние объекты на глубине ниже указанной (по умолчанию 2).

Затем использовать его следующим образом:

public class UnwrappingSerializerTest
{
    public static class BaseT1
    {
        public List<String> getTest()
        {
            return test;
        }

        public void setTest(List<String> test)
        {
            this.test = test;
        }

        private List<String> 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));
    }
}

Примечания:

  • Он ожидает только обертку для одного поля и генерирует недопустимый JSON для чего-то вроде {{field1: {...}, field2: {...}}
  • Если вы используете пользовательский SerializerFactory, вам, вероятно, понадобитсяпередать его сериализатору.
  • Он использует отдельный кеш сериализатора, так что это также может быть проблемой.
0 голосов
/ 28 марта 2013

Я наткнулся на этот вопрос, пытаясь решить ту же проблему, но не использовал его с методом @ResponseBody, но все еще сталкивался с «оберткой» в моем сериализованном JSON.Мое решение состояло в том, чтобы добавить @JsonAnyGetter к методу / полю, и затем оболочка исчезнет из JSON.

По-видимому, это известная ошибка / обходной путь Джексона: http://jira.codehaus.org/browse/JACKSON-765.

0 голосов
/ 24 января 2011

Честно говоря, я бы не стал слишком быстро пытаться исправить эту проблему, поскольку наличие оболочки создает ситуацию, когда ваш код более расширяем Если в будущем вы расширите это для возврата других объектов, вашим клиентам, использующим этот веб-сервис, скорее всего, не потребуется менять реализацию.

Однако, если все клиенты ожидают массив без имени, добавление дополнительных свойств в будущем вне этого массива может нарушить единый интерфейс.

С учетом сказанного, у каждого есть свои причины желать что-то сделать определенным образом. Как выглядит объект, который вы сериализуете? Вы сериализуете объект, который содержит массив, или вы сериализуете сам фактический массив? Если ваш POJO содержит массив, то, возможно, решение состоит в том, чтобы извлечь массив из POJO и вместо этого сериализовать массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...