2013-02-22 19 views
11

Wyobraźmy mam następujący POJO:(de-) Serializować Bean w sposób niestandardowy w czasie wykonywania

class Pojo { 
    String s; 
    Object o; 
    Map<String, String> m; 
} 

A w czasie wykonywania, chcę domyślnej serializacji/deserializacji dla wszystkich nieruchomości z wyjątkiem jednego. Zazwyczaj chcę zastąpić pole identyfikatorem w bazie danych podczas serializacji, podobnie jak this other question.

Na przykład, chcę wymienić o przez ciąg uzyskanej od mapowania zewnętrznego (na przykład: object1 < => "123" i object2 < => "456"):

  • serializacji: czytaj o i zastąpić (więc jeśli o jest object1, szeregować jako ciąg znaków „123”)
  • deserializacji: read „123”, sprawdź jakiś stół, aby uzyskać oryginalną wartość o powrotem (tj object1), odtworzenie Pojo objec tz o = object1.

Rozumiem, że moduły byłby jeden sposób, aby to zrobić, ale nie jestem pewien, w jaki sposób z nich korzystać przy zachowaniu automatycznego BeanSerializer/Deserializator o właściwościach, które nie muszą być zmienione.

Czy ktoś może podać przykład (nawet wymyślony) lub podejście alternatywne?


Uwagi:

  • Nie mogę używać adnotacji lub wstawek jak zmiany są znane w czasie kompilacji (czyli wszelkie właściwości mogą zostać zmienione w taki sposób, że nie można jej ustalić).
  • This other question wskazuje na użycie CustomSerializerFactory, który wydaje się wykonywać zadanie. Niestety oficjalna strona wskazuje, że zamiast tego powinny być używane moduły: it is not the recommended approach any more.

Edit

być nieco jaśniejsze, mogę wykonać następujące czynności wstawkami na przykład:

ObjectMapper mapper = new ObjectMapper(MongoBsonFactory.createFactory()); 
mapper.addMixInAnnotations(Pojo.class, PojoMixIn.class); 

ObjectReader reader = mapper.reader(Pojo.class); 
DBEncoder dbEncoder = DefaultDBEncoder.FACTORY.create(); 
OutputBuffer buffer = new BasicOutputBuffer(); 
dbEncoder.writeObject(buffer, o); 

z następującym mixin:

abstract class PojoMixIn { 
    @JsonIgnore Object o; 
} 

A następnie dodaj wymagany ciąg do treści JSON. Ale będę musiał wiedzieć podczas kompilacji, że to jest pole o, które należy zastąpić, a czego nie.

+0

> Nie mogę używać adnotacji lub wstawek jak zmiany są znane w czasie kompilacji (czyli wszelkie właściwości może być> zmienione w taki sposób, to nie jest możliwe do określenia). Czy możesz wyjaśnić, proszę? Bardzo zdezorientowany, ponieważ wszystko powyżej nagłówka "Notatki:" jest statyczne i znane podczas kompilacji. Czy klasy (Pojo) i pola Pojo nie są znane z góry? –

+0

Sugerujesz, że nie jest bezpiecznie przyjmować żadnej struktury klasowej? Raw (untyped/simple) powiązanie danych deserializuje strukturę JSON obiektu jako LinkedHashMap i strukturę JSON tablicy jako ArrayList , a prymitywne pola JSON jako prymitywne opakowania java. –

+0

@GlenBest Tak, klasa Pojo może być mniej więcej niczym. Podałem przykład, w którym 'o' jest zastępowane przez ciąg. Ale później w programie może to być 'm', które powinno być zastąpione przez jakiś String i' o' serializowane "normalnie". – assylias

Odpowiedz

15

Myślę, że @JsonSerialize i @JsonDeserialize jest tym, czego potrzebujesz. Te adnotacje umożliwiają kontrolę nad serializacją/deserializacją poszczególnych pól. This question pokazuje elegancki sposób łączenia ich w jedną adnotację.

UPD. W tym złożonym scenariuszu można rzucić okiem na klasy BeanSerializerModifier/BeanDeserializerModifier.Chodzi o to, aby zmodyfikować ogólne BeanSerializer/BeanDeserializer z niestandardową logiką dla poszczególnych pól i pozwolić podstawowej implementacji na wykonywanie innych czynności. W późniejszym czasie opublikujemy przykład.

UPD2. Jak widzę, jednym ze sposobów może być użycie metody changeProperties i przypisanie własnego serializera.

UPD3. Zaktualizowany przykład działania niestandardowego serializera. Deserializacji można dokonać w podobny sposób.

UPD4. Zaktualizowany przykład z pełną niestandardową serializacją/deserialization. (Użyłem jakson-mapper-npm-1.9.8)

public class TestBeanSerializationModifiers { 

    static final String PropertyName = "customProperty"; 
    static final String CustomValue = "customValue"; 
    static final String BaseValue = "baseValue"; 

    // Custom serialization 

    static class CustomSerializer extends JsonSerializer<Object> { 
     @Override 
     public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { 
      String customValue = CustomValue; // someService.getCustomValue(value); 
      jgen.writeString(customValue); 
     } 
    } 

    static class MyBeanSerializerModifier extends BeanSerializerModifier { 
     @Override 
     public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BasicBeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) { 
      for (int i = 0; i < beanProperties.size(); i++) { 
       BeanPropertyWriter beanPropertyWriter = beanProperties.get(i); 
       if (PropertyName.equals(beanPropertyWriter.getName())) { 
        beanProperties.set(i, beanPropertyWriter.withSerializer(new CustomSerializer())); 
       } 
      } 
      return beanProperties; 
     } 
    } 

    // Custom deserialization 

    static class CustomDeserializer extends JsonDeserializer<Object> { 
     @Override 
     public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
      // serialized value, 'customValue' 
      String serializedValue = jp.getText(); 
      String baseValue = BaseValue; // someService.restoreOldValue(serializedValue); 
      return baseValue; 
     } 
    } 

    static class MyBeanDeserializerModifier extends BeanDeserializerModifier { 
     @Override 
     public BeanDeserializerBuilder updateBuilder(DeserializationConfig config, BasicBeanDescription beanDesc, BeanDeserializerBuilder builder) { 
      Iterator<SettableBeanProperty> beanPropertyIterator = builder.getProperties(); 
      while (beanPropertyIterator.hasNext()) { 
       SettableBeanProperty settableBeanProperty = beanPropertyIterator.next(); 
       if (PropertyName.equals(settableBeanProperty.getName())) { 
        SettableBeanProperty newSettableBeanProperty = settableBeanProperty.withValueDeserializer(new CustomDeserializer()); 
        builder.addOrReplaceProperty(newSettableBeanProperty, true); 
        break; 
       } 
      } 
      return builder; 
     } 
    } 

    static class Model { 

     private String customProperty = BaseValue; 
     private String[] someArray = new String[]{"one", "two"}; 

     public String getCustomProperty() { 
      return customProperty; 
     } 

     public void setCustomProperty(String customProperty) { 
      this.customProperty = customProperty; 
     } 

     public String[] getSomeArray() { 
      return someArray; 
     } 

     public void setSomeArray(String[] someArray) { 
      this.someArray = someArray; 
     } 
    } 

    public static void main(String[] args) { 
     SerializerFactory serializerFactory = BeanSerializerFactory 
       .instance 
       .withSerializerModifier(new MyBeanSerializerModifier()); 

     DeserializerFactory deserializerFactory = BeanDeserializerFactory 
       .instance 
       .withDeserializerModifier(new MyBeanDeserializerModifier()); 

     ObjectMapper objectMapper = new ObjectMapper(); 
     objectMapper.setSerializerFactory(serializerFactory); 
     objectMapper.setDeserializerProvider(new StdDeserializerProvider(deserializerFactory)); 

     try { 
      final String fileName = "test-serialization.json"; 
      // Store, "customValue" -> json 
      objectMapper.writeValue(new File(fileName), new Model()); 
      // Restore, "baseValue" -> model 
      Model model = objectMapper.readValue(new File(fileName), Model.class); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
+0

Ta informacja jest dostępna tylko w czasie wykonywania? Nieodebrane. – udalmik

+0

Dziękujemy za aktualizację - [BeanPropertyWriter] (http://fasterxml.github.com/jackson-databind/javadoc/2.1.0/com/fasterxml/jackson/databind/ser/BeanPropertyWriter.html) nie ma pliku ' z metodąSerializer'. Może używasz innej wersji? – assylias

+0

'beanPropertyWriter.assignSerializer (nowy MyCustomSerializer());' zmyślił. – assylias

Powiązane problemy