2017-08-15 78 views
6
aşağıdaki POJOs Verilen

..Liste içeriği Jackson ile düz bir JSON nesnesine nasıl serileştirilir?

public class City { 

    private String title; 
    private List<Person> people; 
} 

...

public class Person { 

    private String name; 
    private int age; 
} 

aşağıdaki örneğe JSON sınıflarının Jackson serialize örneklerini izin istiyorum:

{ 
    "title" : "New York", 
    "personName_1" : "Jane Doe", 
    "personAge_1" : 42, 
    "personName_2" : "John Doe", 
    "personAge_2" : 23 
} 

JSON formatı, değiştiremediğim harici bir API tarafından tanımlanmıştır. Ancak

public class PeopleSerializer extends JsonSerializer<List<Person>> { 

    private static final int START_INDEX = 1; 

    @Override 
    public void serialize(List<Person> people, 
          JsonGenerator generator, 
          SerializerProvider provider) throws IOException { 
     for (int i = 0; i < people.size(); ++i) { 
      Person person = people.get(i); 
      int index = i + START_INDEX; 
      serialize(person, index, generator); 
     } 
    } 

    private void serialize(Person person, int index, JsonGenerator generator) throws IOException { 
     generator.writeStringField(getIndexedFieldName("personName", index), 
            person.getName()); 
     generator.writeNumberField(getIndexedFieldName("personAge", index), 
            person.getAge()); 
    } 

    private String getIndexedFieldName(String fieldName, int index) { 
     return fieldName + "_" + index; 
    } 

} 

bu: Denedim temel uygulamasıdır burada

@JsonSerialize(using = PeopleSerializer.class) 
private List<Person> people; 

... ve:

Zaten ben özel bir seri hale gibi olan liste alanını açıklama olduğunu bulduk bir başarısız:

JsonGenerationException: Can not write a field name, expecting a value 

Ben de Jackson'ın Converter arayüzünü ama tha kullanarak içine baktım İç içe geçmiş liste nesnelerini açmak için uygun değildir.

Ben de @JsonUnwrapped farkındayım ama listelerle kullanılmak üzere tasarlanmamıştır.

ilgili mesaj

İlgili mesajlar (seri kaldırma)

İlgili kütüphane

+0

bu çünkü "people" alan adı, daha sonra, "JsonSerializer" öğesinin değerini yazmasını bekler. O güvenilir ve @JsonSerialize (kullanarak = CitySerializer.class) '' 'ile City' sınıfı açıklamalı @ug_ –

+0

. Özel seri hale getirici içinde, sadece yukarıdaki "PeopleSerializer" da yaptığım gibi "insanlar" alanını serileştirdim. Serileştirme aynı hata mesajıyla başarısız oluyor. – JJD

+0

@JJD - Şüphesiz daha basit bir yaklaşım var, çözmeye çalıştığınız kök problem nedir? Ben – AjahnCharles

cevap

1

Doğrudan bir özellik adı ve değer yazılır nasıl değiştirmek için BeanSerializerModifier kullanabilirsiniz. Bunu kullanarak özel bir ek açıklamanın olup olmadığını tespit edebilirsiniz, bu durumda @FlattenCollection olarak adlandırdım. Ek açıklama mevcut olduğunda dizi veya koleksiyon normal yöntem kullanılarak yazılmaz, bunun yerine bir özel özellik yazıcısı tarafından yazılır (FlattenCollectionPropertyWriter). Bu açıklama muhtemelen 2d diziler ya da diğer uç örnekleri üzerinde kıracak

, ben bu test sığınak ama muhtemelen çok sorun olmadan onlar için kod, en azından anlamlı bir hata durum olabilir. İşte tam çalışma kodu

. Önemli noktalar

  • senin için oraya koymak çift Todos FlattenCollectionPropertyWriter.serializeAsField

    • FlattenCollectionSerializerModifier.changeProperties vardır.

    Çıktı:

    { 
        "titleCity" : "New York", 
        "personName_1" : "Foo", 
        "personAge_1" : 123, 
        "personName_2" : "Baz", 
        "personAge_2" : 22 
    } 
    

    Kodu: `için serileştirici City` yazıyor sana` City` sınıfına için `JsonSerializer` yazmak zorunda düşünüyorum

    import com.fasterxml.jackson.core.JsonGenerator; 
    import com.fasterxml.jackson.databind.*; 
    import com.fasterxml.jackson.databind.ser.*; 
    import com.fasterxml.jackson.databind.util.NameTransformer; 
    
    import java.lang.annotation.ElementType; 
    import java.lang.annotation.Retention; 
    import java.lang.annotation.RetentionPolicy; 
    import java.lang.annotation.Target; 
    import java.util.*; 
    
    public class SO45698499 { 
    
    
        public static void main(String [] args) throws Exception { 
         ObjectWriter writer = createMapper().writerWithDefaultPrettyPrinter(); 
         String val = writer.writeValueAsString(new City("New York", 
           Arrays.asList(new Person("Foo", 123), new Person("Baz", 22)))); 
    
         System.out.println(val); 
        } 
    
    
        /** 
        * Constructs our mapper with the serializer modifier in mind 
        * @return 
        */ 
        public static ObjectMapper createMapper() { 
         FlattenCollectionSerializerModifier modifier = new FlattenCollectionSerializerModifier(); 
         SerializerFactory sf = BeanSerializerFactory.instance.withSerializerModifier(modifier); 
         ObjectMapper mapper = new ObjectMapper(); 
         mapper.setSerializerFactory(sf); 
    
         return mapper; 
        } 
    
        @Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) 
        @Retention(RetentionPolicy.RUNTIME) 
        public @interface FlattenCollection { 
        } 
    
        /** 
        * Looks for the FlattenCollection annotation and modifies the bean writer 
        */ 
        public static class FlattenCollectionSerializerModifier extends BeanSerializerModifier { 
    
         @Override 
         public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) { 
          for (int i = 0; i < beanProperties.size(); i++) { 
           BeanPropertyWriter writer = beanProperties.get(i); 
           FlattenCollection annotation = writer.getAnnotation(FlattenCollection.class); 
           if (annotation != null) { 
            beanProperties.set(i, new FlattenCollectionPropertyWriter(writer)); 
           } 
          } 
          return beanProperties; 
         } 
        } 
    
        /** 
        * Instead of writing a collection as an array, flatten the objects down into values. 
        */ 
        public static class FlattenCollectionPropertyWriter extends BeanPropertyWriter { 
         private final BeanPropertyWriter writer; 
    
         public FlattenCollectionPropertyWriter(BeanPropertyWriter writer) { 
          super(writer); 
          this.writer = writer; 
         } 
    
         @Override 
         public void serializeAsField(Object bean, 
                JsonGenerator gen, 
                SerializerProvider prov) throws Exception { 
          Object arrayValue = writer.get(bean); 
    
          // lets try and look for array and collection values 
          final Iterator iterator; 
          if(arrayValue != null && arrayValue.getClass().isArray()) { 
           // deal with array value 
           iterator = Arrays.stream((Object[])arrayValue).iterator(); 
          } else if(arrayValue != null && Collection.class.isAssignableFrom(arrayValue.getClass())) { 
           iterator = ((Collection)arrayValue).iterator(); 
          } else { 
           iterator = null; 
          } 
    
          if(iterator == null) { 
           // TODO: write null? skip? dunno, you gonna figure this one out 
          } else { 
           int index=0; 
           while(iterator.hasNext()) { 
            index++; 
            Object value = iterator.next(); 
            if(value == null) { 
             // TODO: skip null values and still increment or maybe dont increment? You decide 
            } else { 
             // TODO: OP - update your prefix/suffix here, its kinda weird way of making a prefix 
             final String prefix = value.getClass().getSimpleName().toLowerCase(); 
             final String suffix = "_"+index; 
             prov.findValueSerializer(value.getClass()) 
               .unwrappingSerializer(new FlattenNameTransformer(prefix, suffix)) 
               .serialize(value, gen, prov); 
            } 
           } 
          } 
         } 
        } 
    
        public static class FlattenNameTransformer extends NameTransformer { 
    
         private final String prefix; 
         private final String suffix; 
    
         public FlattenNameTransformer(String prefix, String suffix) { 
          this.prefix = prefix; 
          this.suffix = suffix; 
         } 
    
         @Override 
         public String transform(String name) { 
          // captial case the first letter, to prepend the suffix 
          String transformedName = Character.toUpperCase(name.charAt(0)) + name.substring(1); 
          return prefix + transformedName + suffix; 
         } 
         @Override 
         public String reverse(String transformed) { 
          if (transformed.startsWith(prefix)) { 
           String str = transformed.substring(prefix.length()); 
           if (str.endsWith(suffix)) { 
            return str.substring(0, str.length() - suffix.length()); 
           } 
          } 
          return null; 
         } 
         @Override 
         public String toString() { return "[FlattenNameTransformer('"+prefix+"','"+suffix+"')]"; } 
        } 
    
    
        /*=============================== 
        * POJOS 
        ===============================*/ 
        public static class Person { 
         private String name; 
         private int age; 
    
         public Person(String name, int age) { 
          this.name = name; 
          this.age = age; 
         } 
    
         public String getName() { 
          return name; 
         } 
    
         public void setName(String name) { 
          this.name = name; 
         } 
    
         public int getAge() { 
          return age; 
         } 
    
         public void setAge(int age) { 
          this.age = age; 
         } 
        } 
    
        public static class City { 
         private String titleCity; 
         private List<Person> people; 
    
         public City(String title, List<Person> people) { 
          this.titleCity = title; 
          this.people = people; 
         } 
    
         public String getTitleCity() { 
          return titleCity; 
         } 
    
         public void setTitleCity(String titleCity) { 
          this.titleCity = titleCity; 
         } 
    
         @FlattenCollection 
         public List<Person> getPeople() { 
          return people; 
         } 
    
         public void setPeople(List<Person> people) { 
          this.people = people; 
         } 
        } 
    } 
    
  • +0

    WOW! Etkileyici! Serileştirmeyi özelleştirmek için bu seçenek hakkında nereden bilgi edinin? - 'DüzleştirilenKurulumYalnızca Yazıcısı' örneğine kadar sınıfa başarıyla entegre ettim ancak 'serializeAsField() '** ** değil. – JJD

    +0

    @JJD Orijinali nerede bulduğumu unuttum, IDE'mde arama yapmak için hafızamı zengin miktarda java doc ile yeniledim."SerializeAsField()" çağrılmıyorsa, "BeanPropertyWriter" üzerindeki diğer "seralizeAs ***" yöntemlerini kontrol edebilirsiniz. Ayrıca "FlattenCollectionPropertyWriter" in oluşturulduğundan emin olun. Bu yöntemler, javadoc tarafından tanımlanan belirli senaryolarda çağrılacaktı, ancak neden bu mülk için çağrılacaklarını hayal etmekte zorlanıyorum ama bu, bir kırılma noktası oluşturmak için iyi bir yer olurdu. –

    +0

    Bir şekilde yöntem bugün çağrılır. Dün neyin yanlış gittiğini gerçekten söyleyemem. Belki de çok geç oldu. - Cevabınız için teşekkür ederim. Büyük yardım! – JJD

    1

    this link dayanarak ben Alan düzeyinde ek açıklama şüpheli tamamını değil özelliklerini değerini yazma sadece delegeler.

    A (daha doğrusu kludgey) geçici çözüm, tüm İl sınıf için özel bir seri hale sahip olabilir:

    @JsonSerialize(using = CitySerializer.class) 
    public class City { 
        private String title; 
        @JsonIgnore 
        private List<Person> people; 
    } 
    

    ...ve sonra

    public class CitySerializer extends JsonSerializer<City> { 
    
        private static final int START_INDEX = 1; 
    
        @Override 
        public void serialize(City city, 
              JsonGenerator generator, 
              SerializerProvider provider) throws IOException { 
         generator.writeStartObject(); 
    
         // Write all properties (except ignored) 
         JavaType javaType = provider.constructType(City.class); 
         BeanDescription beanDesc = provider.getConfig().introspect(javaType); 
         JsonSerializer<Object> serializer = BeanSerializerFactory.instance.findBeanSerializer(provider, 
           javaType, 
           beanDesc); 
         serializer.unwrappingSerializer(null).serialize(value, jgen, provider);` 
    
         // Custom serialization of people 
         List<Person> people = city.getPeople(); 
         for (int i = 0; i < people.size(); ++i) { 
          Person person = people.get(i); 
          int index = i + START_INDEX; 
          serialize(person, index, generator); 
         } 
    
         generator.writeEndObject(); 
        } 
    
        private void serialize(Person person, int index, JsonGenerator generator) throws IOException { 
         generator.writeStringField(getIndexedFieldName("personName", index), 
                person.getName()); 
         generator.writeNumberField(getIndexedFieldName("personAge", index), 
                person.getAge()); 
        } 
    
        private String getIndexedFieldName(String fieldName, int index) { 
         return fieldName + "_" + index; 
        } 
    
    } 
    
    +0

    Teşekkürler, bu umut verici görünüyor. Ancak, büyük bir dezavantajı, "insanlar" alanı için özel muameleyi tanımlayamam. Ayrıca 'City' sınıfının ** diğer tüm alanlarını (gerçekte daha fazla var) elle yazmam gerekiyor. 'City' sınıfı değiştiğinde, serileştiriciyi de güncellemeliyim. Bu korumak zorlaştırır. Ayrıca ele [buradan] (https://stackoverflow.com/questions/14714328/jackson-how-to-add-custom-property-to-the-json-without-modifying-the-pojo). Sadece ** kesişme ** City' 'serileştirme ve elle' halkındaki alanın özel JSON gösterimi yazabilirsem – JJD

    +0

    harika olurdu. Bu, belirli bir alan için * delegasyon serializer * olarak da adlandırılabilir. Belki de sadece yanlış terimleri aradım. – JJD

    +0

    @JJD - Sana tamamen katılıyorum. Üstünde bir bıçak var, ama şu anda kolayca test edemiyorum. – AjahnCharles