2011-01-22 10 views
7

Mam usługę WWW, która zwraca listę jako JSON. Używa Jackson do odwzorowania listy POJO Java w JSON. Problem polega na tym, że reprezentacja JSON ma obiekt otaczający wokół tablicy, a ja po prostu chcę tablicy. To znaczy, ja dostaję to:Pomijanie obiektu opakowania podczas serializacji obiektu Java do JSON przy użyciu usługi Jackson

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

kiedy to, co naprawdę chcę to:

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

ja szeregowania listy Java bezpośrednio; Nie pakuję List w obiekt opakowania i serializuję obiekt opakowania. To Jackson wydaje się dodawać obiekt wrappera JavaScript.

Zakładam, że istnieje adnotacja, której mogę użyć w POJO do stłumienia obiektu opakowania, ale go nie widzę.

Ograniczenia dotyczące rozwiązania

które chciałbym, aby to naprawić na stronie serwisu zamiast oderwanie opakowanie na kliencie. Klient jest widgetem JQuery UI (widżet autouzupełniania, nie to ma znaczenia), który oczekuje prostej tablicy i nie chcę modyfikować samego widżetu.

Co Próbowałem

  • Próbowałem zastępując Lista POJOs Java z tablicą POJOs Javy, a wynik jest taki sam.
  • Próbowałem @JsonTypeInfo(use = Id.NONE) myśląc, że to może powstrzymać opakowanie, ale tak się nie stało.
+1

To brzmi jak serializacja niewłaściwego obiektu. Dlaczego nie możesz po prostu użyć Mappera do serializowania listy bezpośrednio zamiast serializacji otaczającego POJO? –

+0

Sumeralizuję listę bezpośrednio. POJO są na liście. Problem polega na tym, że gdy serializuję listę, Jackson dodaje do niej opakowanie. Myślałem, że być może Jackson sam odróżniał obiekt listy od swojej tablicy, więc nawet spróbowałem serializować tablicę bezpośrednio. Brak szczęścia; Jackson nadal dodaje obiekt opakowania. –

+0

Czy możesz pokazać nam kod? Używam Jackson dość często i nigdy nie doświadczyłem tego, co opisujesz. –

Odpowiedz

5

W trybie testowym podczas uruchamiania:

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

powraca:

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

który jest zachowanie spodziewasz prawda?

Mogłem zobaczyć, że gdy zwrócę tę listę za pomocą kontrolera Spring i pozwól MappingJacksonJsonView uchwyt przekształca listę do json, to tak jest wokół niego opakowanie, które mówi mi, że MappingJacksonJsonView to ten, który dodaje opakowanie. Jednym z rozwiązań byłoby wtedy jawne zwrócenie json z kontrolera, powiedzmy:

@RequestMapping(value = "/listnowrapper") 
public @ResponseBody String listNoWrapper() throws Exception{  
    ObjectMapper mapper = new ObjectMapper(); 
    return mapper.writeValueAsString(Arrays.asList("one","two","three","four","five")); 
} 
+0

To jest dokładnie to, o czym mówiłem. Serializuj tablicę zamiast obiektu zawierającego tablicę. +1 – jmort253

+0

Dzięki chłopaki. To musi być to, co się dzieje. Rzeczywiście używam MappingJacksonJsonView i po prostu założyłem, że Jackson był tutaj przestępcą. :-) Doceń pomoc. –

0

Szczerze mówiąc, nie będzie zbyt szybkie, aby spróbować fix ten problem jako posiadające opakowanie stwarza sytuację, w której kod jest bardziej rozciągliwe. Jeśli rozszerzysz to w przyszłości, aby zwrócić inne obiekty, klienci używający tej usługi najprawdopodobniej nie będą musieli zmieniać implementacji.

Jeśli jednak wszyscy klienci oczekują tablicy bez nazwy, dodanie większej liczby właściwości w przyszłości poza tą tablicą może spowodować złamanie jednolitego interfejsu.

Po tym wszystkim każdy ma swoje powody, by chcieć zrobić coś w określony sposób. Jak wygląda obiekt, który serializuje? Czy serializujesz obiekt zawierający tablicę, czy też serializujesz samą tablicę? Jeśli twoje POJO zawiera tablicę, to może rozwiązaniem jest wyciągnięcie tablicy z POJO i serializacja tablicy.

+0

Tak, moim powodem jest to, że kontrakt nie jest do zgarnięcia - komponent klienta oczekuje surowej tablicy. Serializuję samą tablicę. Jackson irytująco dodaje opakowanie. –

1

można napisać niestandardowy serializatora:

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

To będzie ignorować obiekty zewnętrzne na głębokości niższej niż określona (2 domyślnie).

następnie używać go w sposób następujący:

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

Uwagi:

  • spodziewa tylko pojedynczy wrapper pola i będzie generować nieprawidłową JSON na czymś jak {{field1: {...}, field2: {...}}
  • Jeśli używasz niestandardowego SerializerFactory was prawdopodobnie będzie musiał przekazać go do serializera.
  • Używa oddzielnej pamięci podręcznej serializera, więc może to również stanowić problem.
+0

OK, przyjrzę to. Mam nadzieję, że jest coś prostszego - niestandardowe podejście serializera wydaje się być więcej pracy niż wygenerowanie JSON ręcznie. (Chociaż możemy rozłożyć koszty, gdy wdrażamy więcej mapppings.) –

5

Mam taki sam problem jak ty.

Po dodaniu @ResponseBody przed moją listą w deklaracji metody, problem został rozwiązany.

np

public @ResponseBody List<MyObject> getObject

+0

Dzięki za to ... rozwiązałeś mój problem! – Paul

0

natknąłem się na to pytanie, starając się rozwiązać ten sam problem, ale nie było to z wykorzystaniem metody @ResponseBody, ale nadal napotykają na „opakowanie” W mojej odcinkach JSON. Moim rozwiązaniem było dodanie @JsonAnyGetter do metody/pola, a następnie opakowanie zniknie z JSON.

Podobno jest to znany błąd Jacksona/obejście problemu: http://jira.codehaus.org/browse/JACKSON-765.

Powiązane problemy