Alexiuscrow
@Alexiuscrow

Как написать кастомный сериализатор объектов entity классов с отношениями?

В своём веб-проекте я использую hiberante и jersey, который в свою очередь идёт вместе с Jackson. Для работы с hiberante у меня есть 3 entity-класса, которые отображают структура таблиц базы данных и имею отношения "один ко многим" и соответственно "многие к одному". Связующие поля отмечены аннотациями @OneToMany и @ManyToOne. Объекты этих классов должны быть сериализованы в json. Для избежания ошибок во время сериализации связующие поля были также отмечены аннотациями @JsonManagedReference и @JsonBackReference.
В результате сериализации объекта класса Shops получалась такая строка:
{
      "id": 2,
      "name": "Shop #2",
      "category": "auto",
      "distance": 120,
      "discounts": [
          {
              "id": 9,
              "title": "-50%",
              "startDate": 1425679200000,
              "endDate": 1425679200000
          }
      ]
 }

Меня это не устраивало и я решил написать свой кастомный сериализатор. В результате я хотел видеть следующий json:
{
      "id": 2,
      "name": "Shop #2",
      "category": "auto",
      "distance": 120,
      "locality_id": 10,
 }


Для этого я создал 3 класса-сериализатора для каждого из entity и убрал аннотации @JsonManagedReference и @JsonBackReference, которые были раньше и добавил @JsonSerialize с указание класса-сериализатора.

В последствии при попытке запуска я получаю следующую ошибку:
com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: java.util.ArrayList[0])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:189)
at com.fasterxml.jackson.databind.ser.std.StdSerializer.wrapAndThrow(StdSerializer.java:216)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:117)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:73)
at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:19)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:129)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1052)


Как я могу исправить данную ошибку?
Ниже приведены entity-классы и классы-сериализаторы.

Entity-классы:

Localities
@XmlRootElement
@Entity
@JsonSerialize(using = LocalitiesSerializer.class)
@Table(name = "localities")
public class Localities {
    @Id
    @GeneratedValue
    @Column(name="id", unique=true, nullable=false)
    private Integer id;

    @Column(name="name",nullable=false, length=57)
    private String name;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "locality")
    private Set<Shops> shops = new HashSet<Shops>(0);

    //constructors, getters & setters
}

Shops
@XmlRootElement
@Entity
@JsonSerialize(using = ShopsSerializer.class)
@Table(name = "shops")
public class Shops {
    @Id
    @GeneratedValue
    @Column(name="id", unique=true, nullable=false)
    protected Integer id;

    @Column(name="name", length=45, nullable=false)
    protected String name;

    @Column(name="category", columnDefinition="ENUM('undefined','auto','children_prod','food','game','book','electronics','beuty_and_health','fashion','footwear','clothing','sports','homewere','pet_prod','services','gift_and_flowers')",
            nullable=false)
    @Enumerated(EnumType.STRING)
    protected Categories category;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "locality_id", nullable = false)
    protected Localities locality;

    @Transient
    private Double distance;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "shop")
    private Set<Discounts> discounts = new HashSet<Discounts>(0);

    //constructors, getters & setters    
}

Discount
@XmlRootElement
@Entity
@JsonSerialize(using = DiscountsSerializer.class)
@Table(name="discounts")
public class Discounts {

    @Id
    @GeneratedValue
    @Column(name="id")
    private Integer id;

    @Column(name="title")
    private String title;

    @Column(name="start_date")
    @Temporal(TemporalType.DATE) 
    private Calendar startDate;

    @Column(name="end_date")
    @Temporal(TemporalType.DATE) 
    private Calendar endDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "shop_id", nullable = false)
    private Shops shop;

    //constructors, getters & setters

}


Классы-сериализаторы:

LocalitiesSerializer
public class LocalitiesSerializer extends JsonSerializer<Localities> {
    @Override
    public void serialize(Localities locality, JsonGenerator jsonGen,
            SerializerProvider serProv) throws IOException,
            JsonProcessingException {
        jsonGen.writeStartObject();
        jsonGen.writeNumberField("id", locality.getId());
        jsonGen.writeStringField("name", locality.getName());
        jsonGen.writeEndObject();

    }
}

ShopsSerializer
public class ShopsSerializer extends JsonSerializer<Shops> {

    @Override
    public void serialize(Shops shop, JsonGenerator jsonGen,
            SerializerProvider serProv) throws IOException,
            JsonProcessingException {
        jsonGen.writeStartObject();
        jsonGen.writeNumberField("id", shop.getId());
        jsonGen.writeStringField("name", shop.getName());
        jsonGen.writeStringField("category", shop.getCategory().toString());
        jsonGen.writeNumberField("distance", shop.getDistance());
        jsonGen.writeNumberField("locality_id", shop.getLocality().getId());
        jsonGen.writeEndObject();

    }

}

DiscountsSerializer
public class DiscountsSerializer extends JsonSerializer<Discounts> {
    @Override
    public void serialize(Discounts discount, JsonGenerator jsonGen,
            SerializerProvider serProv) throws IOException,
            JsonProcessingException {

        jsonGen.writeStartObject();
        jsonGen.writeNumberField("id", discount.getId());
        jsonGen.writeStringField("title", discount.getTitle());
        jsonGen.writeStringField("start_date", discount.getStartDate().toString());
        jsonGen.writeStringField("end_date", discount.getEndDate().toString());
        jsonGen.writeNumberField("shop_id", discount.getShop().getId());
        jsonGen.writeEndObject();

    }
}

P.S. Аннотация @JsonIgnore в данном случае не подходит тк в дальнейшем я планирую убрать аннотации @JsonSerialize и использовать классы-сериализаторы следующим образом:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Item.class, new ShopsSerializer());
mapper.registerModule(module);
String serialized = mapper.writeValueAsString(shop);

Таким образом появляется возможность использовать разные сериализаторы.
  • Вопрос задан
  • 1256 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы