Сериализуйте один класс двумя разными способами с помощью Jackson

В одном из наших проектов мы используем веб-приложение java, общающееся с экземпляром MongoDB. В базе данных мы используем DBRefs для отслеживания некоторых объектных отношений. Мы (де) сериализуем объекты POJO с помощью jackson (используя mongodb-jackson-mapper).

Однако мы используем те же POJO для последующей (де) сериализации во внешний мир, где наш интерфейс имеет дело с представлением JSON.

Теперь нам нужен способ сериализации для внешнего мира, чтобы он содержал указанный объект из DBRef (чтобы пользовательский интерфейс мог представить весь объект), в то время как мы, очевидно, хотим, чтобы DBRef был записан в базу данных, а не весь объект.

Прямо сейчас я написал непроверенный статический код вложенного класса:

public static class FooReference {
    public DBRef<Foo> foo;

    // FIXME how to ensure that this doesn't go into the database?
    public Foo getFoo() {
        return foo.fetch();
    }
}

В идеале мне нужен способ аннотировать это, чтобы я мог (де) сериализовать его либо с результатом getFoo (), либо без него, вероятно, в зависимости от некоторого объекта конфигурации. Это возможно? Вы видите лучший способ сделать это?


person Gijs    schedule 27.08.2012    source источник
comment
Как мне это сделать и как это решить проблему? Это мой код, поэтому я могу делать что угодно, но ваш комментарий непонятен, и Google не помогает, извините.   -  person Gijs    schedule 27.08.2012
comment
transient - это ключевое слово java, указывающее, что поле не должно сериализоваться / сохраняться - java -samples.com/showtutorial.php?tutorialid=331   -  person Nicholas Albion    schedule 27.08.2012
comment
Эмм, это модификатор поля. Это метод.   -  person Gijs    schedule 27.08.2012
comment
Есть ли у mongodb аннотация для методов Transient или NonPersistant?   -  person Nicholas Albion    schedule 27.08.2012
comment
У самого Mongo есть драйверы (которые работают с простыми объектами, подобными карте), которые были расширены / заменены / упакованы различными способами для обеспечения поддержки POJO. Оказывается, у Morphia есть аннотации Transient и NotStored, но я использую mongo-jackson-mapper. Еще немного поисков в Google показывает, что Джексон поддерживает представления для сериализации, а mongo-jackson-mapper позволяет указать представление для использования, так что кажется, что это сработает. Спасибо за указатели!   -  person Gijs    schedule 27.08.2012


Ответы (2)


Глядя на параметры, кажется, что вы можете аннотировать свойства, чтобы они отображались только в том случае, если заданный View передается в ObjectMapper, используемый для сериализации. Таким образом, вы можете отредактировать класс:

public static class FooReference {
    public DBRef<Foo> foo;

    @JsonView(Views.WebView.class)
    public Foo getFoo() {
        return foo.fetch();
    }
}

и предоставить:

class Views {
    static class WebView { }
}

а затем сериализуйте после создания конфигурации с правильным представлением:

SerializationConfig conf = objectMapper.getSerializationConfig().withView(Views.WebView.class);
objectMapper.setSerializationConfig(conf);

Которая затем сериализует его. Отсутствие указания представления при сериализации с помощью оболочки MongoDB будет означать, что метод будет проигнорирован. Свойства без аннотации JsonView по умолчанию сериализуются, и это поведение можно изменить, указав:

objectMapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);

Дополнительная информация доступна в Jackson Wiki.

Оказывается, есть и другие альтернативы: есть Jackson MixIns, которые позволят вам переопределить (de ) поведение сериализации частей класса без изменения самого класса, а с Jackson 2.0 (совсем недавний выпуск) существуют фильтры тоже.

person Gijs    schedule 27.08.2012

Используйте собственный JSONSerializer и примените свою логику в методе serialize:

public static class FooReference {
    public DBRef<Foo> foo;

    @JsonSerialize(using = CustomSerializer.class)
    public Foo getFoo() {
        return foo.fetch();
    }
}

public class CustomSerializer extends JsonSerializer<Object> {
   public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider)
       throws IOException, JsonProcessingException {
     // jgen.writeObjectField ...
   }
}
person João Silva    schedule 27.08.2012
comment
Разве это не вызовет getFoo() безоговорочно? Это было бы неоптимально, поскольку подразумевает запрос к базе данных. Кроме того, насколько я могу судить, сериализатор будет построен с использованием класса ref, и поэтому у него нет контекста, в котором он мог бы сделать вывод, следует ли сериализовать объект. Так что мне нужно полагаться на какой-то статический флаг, присутствующий в том же потоке, который я вертел перед / после сериализации / десериализации? Это кажется очень хрупким. - person Gijs; 27.08.2012
comment
Да, я неправильно понял ваш вопрос. Но вместо возврата foo.fetch() вы можете вернуть оболочку, которая в serialize определит, нужно ли вам сделать запрос к базе данных или сериализовать поле. - person João Silva; 27.08.2012
comment
Это звучит хорошо в отношении первого замечания, которое я сделал. Что касается второго: в идеале мне не нужны какие-либо данные в модели / POJO относительно того, что происходит вне модели (IE, как я сериализуюсь). То есть я бы предпочел «выбрать» один из двух методов сериализации, когда я вызываю его (де) сериализацию. Имеет ли это смысл и возможно ли это с помощью описанного вами метода? (в любом случае проголосовали за указание на JsonSerialize, но пока не уверен, что это решение этой проблемы) - person Gijs; 27.08.2012
comment
Я слежу за вами, этот метод можно использовать, но он действительно каким-то образом соединит механизм сериализации с моделью. Единственный механизм, который я знаю в Джексоне и который позволяет вам это делать, на самом деле JSONSerializer, поэтому я боюсь, что вам придется поместить туда свою собственную логику. - person João Silva; 27.08.2012