2014-11-16 2 views
1

У меня есть относительно сложный объект, который содержит несколько полей. Мне нужно сериализовать одно из полей, используя собственный сериализатор, но нужно эмулировать функциональность @JsonUnwrapped.@JsonUnwrapped for Custom Serializer

Для простоты я сократить это до двух полей:

public class MyClass 
{ 
    @JsonProperty("subject") 
    private final String subject; 
    @JsonSerialize(using=MySenderSerializer.class) 
    private final MailActor sender; 
} 

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

public class MySenderSerializer extends StdSerializer<MailActor> 
{ 
    public MySenderSerializer() 
    { 
    super(MailActor.class, true); 
    } 

    @Override 
    public void serialize(final MailActor value, final JsonGenerator gen, final SerializerProvider provider) throws IOException 
    { 
    gen.writeStringField("from_name", value.getName()); 
    gen.writeStringField("from_email", value.getAddress()); 
    } 
} 

Все это прекрасно, за исключением того, что выход JSON выглядит следующим образом:

{ 
    ... 
    "subject": "test subject", 
    "sender": { 
    "from_name": "test from", 
    "from_email": "[email protected]" 
    }, 
    ... 
} 

и мне нужно разворачивать sender поле так что JSON выглядит следующим образом:

{ 
    ... 
    "subject": "test subject", 
    "from_name": "test from", 
    "from_email": "[email protected]", 
    ... 
} 

Если я использую стандартные сериализаторы я мог бы использовать @JsonUnwrapped аннотации, чтобы сделать это, но он не появляется, чтобы быть совместимыми с пользовательскими сериализаторами. Как я могу получить требуемый вывод JSON без записи пользовательского сериализатора для объекта MyClass?

ответ

0

Ну, это потому, что вы разработали его таким образом, когда Джексон отображает объект, он отображает внутренние свойства в качестве под-свойств этого объекта, если вы хотите, чтобы он сериализовал эти два поля, как если бы они были членов MyClass вместо MailActor, а затем объявите их как таковые.

Это может указывать на то, что ваш дизайн объекта может иметь некоторые небольшие недостатки.

Я бы написал custome-сериализатор для объекта MyClass, но все же, в конечном счете, это не жизнеспособное решение.

+0

Я бы не сказал, что у объекта есть недостатки; он отлично работает в других областях, включая другие вызовы в одном API, и проблема подходит к ожидаемому формату для конкретного вызова в конкретном API. – jgm

+0

Тогда это должен быть другой объект, я полагаю. Поскольку вы не хотите, чтобы объект Mail с объектом Sender находился внутри, вы хотите, чтобы объект Mail с отсутствием свойств отправителя, я действительно не рассматриваю его как проблему разбора json, что я подразумеваю под дизайном объекта. –

+0

Спасибо за продолжение ответа, но моя борьба с Джексоном не является объектной моделью. Та же самая информация (имя и адрес электронной почты) отображается в разных частях API в разных форматах: иногда завернуты, а иногда нет, и с разными именами полей. Независимо от того, что я выбираю в качестве дизайна объекта, у меня будет эта проблема, и это проблема, которая обычно рассматривается в Jackson с помощью '@ JsonUnwrapped', но это не очень хорошо сочетается с пользовательскими сериализаторами. Следовательно, вопрос, есть ли что-то эквивалентное, я могу сделать с помощью настраиваемого сериализатора. – jgm

1

Мне пришлось искать альтернативу @JsonUnwrapped тоже, поскольку это вызывало у меня некоторые не связанные с этим вопросы.

Решение, которое я внедрил, применимо к вашему делу аналогичным образом, используя @JsonAnyGetter. В вашем MyClass игнорировать атрибут, который нуждается в специальной сериализации и вместо того, чтобы добавить его с JsonAnyGetter:

public class MyClass 
{ 
    @JsonProperty("subject") 
    private final String subject; 
    @JsonIgnore 
    private final MailActor sender; 

    @JsonAnyGetter 
    public Map<String, Object> serializeActor() { 
     return sender.serializeToMap(); 
     // Of course, here you could create an empty map 
     // and add the properties of many different classes. 
    } 
} 

Тогда в классе MailActor вы реализовать этот метод, который будет возвращать карту со свойствами, которые вы можете.

public Map<String, Object> serializeToMap() { 
    final Map<String, Object> properties = new ArrayMap<>(); 
    properties.put("from_name", this.getName()); 
    properties.put("from_email", this.getAddress()); 
    return properties; 
} 

Проблема происходит этот способ десериализации объекта, так как @JsonAnySetter не получает карту с JSON в карте так же, как вы даете его в примере выше. В вашем случае это еще сложнее, так как атрибуты вашего класса являются окончательными. Вам нужно будет использовать @JsonCreator, который должен был бы создать все ваши атрибуты. Что-то вдоль линий:

@JsonCreator 
public MyClass(Map< String, String > json) { 
    if (json.containsKey("subject")) { 
     subject = json.get("subject"); 
    } else { 
     subject = ""; 
    } 

    String name, address; 
    if (json.containsKey("from_name")) { 
     name = json.get("from_name"); 
    } else { 
     name = ""; 
    } 
    if (json.containsKey("from_email")) { 
     address = json.get("from_email"); 
    } else { 
     address = ""; 
    } 
    sender = new MailActor(name, address); 
} 

Это было время, так как вы разместили вопрос, надеюсь, что это полезно в некотором роде к кому-то наступающем искать альтернативные варианты.