2013-09-26 4 views
2

У меня есть класс, который при сериализации должен сериализовать один из его членов на своем месте. Мой класс:Пользовательский сериализатор с MongoDB, Jackson и MongoJack

@JsonSerialize(using = MyClassSerializer.class) 
public class MyClass implements Serializable { 

    /** 
    * A default ID for this class for serialization. 
    */ 
    private static final long serialVersionUID = 1L; 

    /** 
    * A member of this object. 
    */ 
    private final OtherClass otherClass; 

    ... 

    /** 
    * Returns the instance of the member object. 
    * 
    * @return The instance of the member object. 
    */ 
    public OtherClass getOtherClass() { 
     return otherClass; 
    } 
} 

Для достижения этой цели, я создал очень простой пользовательский сериалайзер:

public class MyClassSerializer extends JsonSerializer<MyClass> { 
    /** 
    * Serializes only the OtherClass field. 
    */ 
    @Override 
    public void serialize(
     final MyClass myClass, 
     final JsonGenerator generator, 
     final SerializerProvider provider) 
     throws IOException, JsonProcessingException { 

     // Write the schema. 
     generator.writeObject(myClass.getOtherClass()); 
    } 
} 

Это самый простой (и, я считаю, самый правильный) способ сделать что-то вроде этого. Даже если бы я захотел, писать пользовательский сериализатор для OtherClass был бы чрезвычайно сложным, потому что он является абстрактным корневым классом. Это может быть достигнуто через Джексон, однако, с несколькими примечаниями:

@JsonTypeInfo(
    use = Id.NAME, 
    include = As.PROPERTY, 
    property = OtherClass.JSON_KEY_TYPE, 
    defaultImpl = OtherClassDefault.class) 
@JsonSubTypes({ 
    @JsonSubTypes.Type(
     value = SubOtherClass1.class, 
     name = SubOtherClass1.TYPE_ID), 
    @JsonSubTypes.Type(
     value = SubOtherClass2.class, 
     name = SubOtherClass2.TYPE_ID), 
    @JsonSubTypes.Type(
     value = SubOtherClass3.class, 
     name = SubOtherClass3.TYPE_ID), 
    @JsonSubTypes.Type(
     value = SubOtherClass4.class, 
     name = SubOtherClass4.TYPE_ID), 
    @JsonSubTypes.Type(
     value = SubOtherClass5.class, 
     name = SubOtherClass5.TYPE_ID) }) 
@JsonAutoDetect(
    fieldVisibility = Visibility.DEFAULT, 
    getterVisibility = Visibility.NONE, 
    setterVisibility = Visibility.NONE, 
    creatorVisibility = Visibility.DEFAULT) 
public abstract class OtherClass implements Serializable { 
    ... 
} 

Я испытал это с помощью Джексона сериализации и десериализации экземпляры MyClass, и она работает точно так же, как предполагалось.

Мой код приложения немного сложнее. У меня есть ContainerClass, который имеет тип MyClass. Я пытаюсь сериализация экземпляра ContainerClass через MongoJack:

// Get the authentication token collection. 
JacksonDBCollection<ContainerClass, Object> collection = 
    JacksonDBCollection 
     .wrap(
      MongoBinController 
       .getInstance() 
       .getDb() 
       .getCollection(COLLECTION_NAME), 
      ContainerClass.class); 

// Save it. 
collection.insert(container); 

Однако, я получаю следующее сообщение об ошибке:

... 
Caused by: java.lang.IllegalArgumentException: can't serialize class my.package.MyClass 
    at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:270) 
    ... 

я могу заставить его работать, если я удалить @JsonSerialize из MyClass, однако это результатов во всем экземпляре MyClass, который не является тем, что я хочу. Это делает меня почти уверенным, что проблема заключается в моем пользовательском сериализаторе, но я не уверен, как еще я должен его написать.

Заранее спасибо.

ответ

1

Одно небольшое замечание, что может помочь: при использовании полиморфных типов, метод, называемый будет:

serializeWithType(...) 

и не serialize(...). Таким образом, вам нужно будет реализовать этот метод; как правило, это будет как-то просто, как:

typeSer.writeTypePrefixForObject(value, jgen); 
// implement actual content serialization, or delegate here: 
this.serialize(...); 
typeSer.writeTypeSuffixForObject(value, jgen); 

но вы можете захотеть взглянуть на стандартных сериализаторах Джексона. Единственное реальное отличие заключается в том, что тогда, когда serialize должен выводить START_OBJECT, END_OBJECT напрямую, здесь мы должны задать TypeSerializer, чтобы добавить их. Это необходимо, потому что включение type id может фактически изменить их (я могу подробно остановиться на этом, но на данный момент этого должно быть достаточно).

Однако: здесь может быть намного проще. Если вы можете добавить @JsonValue аннотации члена вы хотите использовать вместо всего объекта (либо непосредственно, либо с помощью смеси-в), что следует сделать трюк:

@JsonValue 
public OtherClass getOtherClass()... 

и если вам нужно десериализация, вы можете использовать @JsonCreator:

@JsonCreator 
public MyClass(OtherClass surrogate) { ... } 
+1

Отлично! Это ответило на мой первоначальный вопрос; Я не могу поверить, что никогда не знал о '@ JsonValue'. Я попробовал '@ JsonCreator', но у меня проблемы.Он жалуется, что абстрактные типы либо должны быть сопоставлены конкретным типам (которые ** ** выполняются в аннотациях абстрактного класса), имеют собственный десериализатор (что, вероятно, то, что я буду делать), либо создаются с помощью дополнительных (что я не понимаю). В любом случае, если у вас есть предложение о том, как исправить это, это будет здорово, но если нет, пользовательский десериализатор уже работает. Еще раз спасибо! – user1236874

+0

Возможно, стоит задать другой вопрос здесь или в списках рассылки: я не знаю, что делать, но это должно быть возможным, независимо от проблемы. Но с пользовательским десериализатором, возможно, не стоит беспокоиться об этом. – StaxMan

Смежные вопросы