Несколько разных схем Джексона для сериализации вложенных полей - PullRequest
1 голос
/ 12 июля 2020

Я хочу иметь несколько разных схем при сериализации с помощью Джексона. Предположим, у меня есть следующие классы:

public class Department {
      private Person head;
      private Person deputy;
      private List<Person> staff;
      // getters and setters
}

public class Person {
       private String name
       private int code;
      // getters and setters
}

Теперь я хочу иметь две разные схемы для класса Department. Первый содержит только head и deputy, где head включает как name, так и code, а deputy содержит только name. Вторая схема должна включать все поля рекурсивно.

Таким образом, у нас будет два разных json'а. С первой схемой:

{
    "head" : {
        "name" : "John",
        "code" : 123
     },
     "deputy" : { 
        "name" : "Jack"
     } 
}

, а со второй схемой:

{
    "head" : {
        "name" : "John",
        "code" : 123
     },
     "deputy" : { 
        "name" : "Jack",
        "code" : "234"
     },
     "staff": [
        { 
            "name" : "Tom",
            "code" : "345"
         },
         { 
            "name" : "Matt",
            "code" : "456"
         },
     ]
}

ВОПРОС: Как мне сделать это с Джексоном?

ПРИМЕЧАНИЕ: Эти классы являются лишь примерами. В этом простом примере можно написать четыре разных класса-оболочки, но подумайте о сложном примере с дюжиной классов, каждый из которых имеет несколько полей. Используя классы-оболочки, мы должны сгенерировать много шаблонного кода.

Любая помощь приветствуется!

Ответы [ 2 ]

1 голос
/ 26 июля 2020

Хотя решение @bigbounty отличное, и я думаю, что View s, в дополнение к c DTO s, являются способом go в целом, в этом случае оно может быть неприменимо, потому что , для одного и того же класса Person нам действительно нужны два разных поведения в одном и том же представлении.

@JsonFilter s можно использовать для решения проблемы.

Это Метод main вычисляет график, который вам нужен:


  public static void main(String[] args) throws JsonProcessingException {

    final PropertyFilter departmentFilter = new SimpleBeanPropertyFilter() {
      @Override
      public void serializeAsField
        (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
        throws Exception {
        if (include(writer)) {
          final String name = writer.getName();
          if (!name.equals("deputy") && !name.equals("staff")) {
            writer.serializeAsField(pojo, jgen, provider);
            return;
          }

          if (name.equals("staff")) {
            return;
          }

          // Ideally it should not be muted.
          final Department department = (Department)pojo;
          final Person deputy = department.getDeputy();
          deputy.setCode(-1);

          writer.serializeAsField(department, jgen, provider);

        } else if (!jgen.canOmitFields()) { // since 2.3
          writer.serializeAsOmittedField(pojo, jgen, provider);
        }
      }
      @Override
      protected boolean include(BeanPropertyWriter writer) {
        return true;
      }
      @Override
      protected boolean include(PropertyWriter writer) {
        return true;
      }
    };

    final PropertyFilter personFilter = new SimpleBeanPropertyFilter() {

      @Override
      public void serializeAsField
        (Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer)
        throws Exception {
        if (include(writer)) {
          if (!writer.getName().equals("code")) {
            writer.serializeAsField(pojo, jgen, provider);
            return;
          }

          int code = ((Person) pojo).getCode();
          if (code >= 0) {
            writer.serializeAsField(pojo, jgen, provider);
          }
        } else if (!jgen.canOmitFields()) { // since 2.3
          writer.serializeAsOmittedField(pojo, jgen, provider);
        }
      }
      @Override
      protected boolean include(BeanPropertyWriter writer) {
        return true;
      }
      @Override
      protected boolean include(PropertyWriter writer) {
        return true;
      }
    };

    final Department department = new Department();
    final Person head = new Person("John", 123);
    final Person deputy = new Person("Jack", 234);
    final List<Person> personList = Arrays.asList(new Person("Tom", 345), new Person("Matt", 456));
    department.setHead(head);
    department.setDeputy(deputy);
    department.setStaff(personList);

    final ObjectMapper mapper = new ObjectMapper();

    final FilterProvider schema1Filters = new SimpleFilterProvider()
      .addFilter("deparmentFilter", departmentFilter)
      .addFilter("personFilter", personFilter)
      ;

    mapper.setFilterProvider(schema1Filters);

    final String withSchema1Filters = mapper.writeValueAsString(department);
    System.out.printf("Schema 1:\n%s\n", withSchema1Filters);

    // You must maintain the filters once the classes are annotated with @JsonFilter
    // We can use two no-op builtin filters
    final FilterProvider schema2Filters = new SimpleFilterProvider()
      .addFilter("deparmentFilter", SimpleBeanPropertyFilter.serializeAll())
      .addFilter("personFilter", SimpleBeanPropertyFilter.serializeAll())
      ;

    mapper.setFilterProvider(schema2Filters);

    final String withSchema2Filters = mapper.writeValueAsString(department);
    System.out.printf("Schema 2:\n%s\n", withSchema2Filters);
  }

Чтобы этот код работал, вы должны аннотировать класс Department с помощью:

@JsonFilter("deparmentFilter")

И Person class с:

@JsonFilter("personFilter")

Как видите, Джексон также предоставляет несколько встроенных фильтров.

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

Пожалуйста, посмотрите SimpleBeanPropertyFilter , чтобы увидеть примеры того, как создать свой собственный фильтр.

0 голосов
/ 13 июля 2020

В библиотеке Джексона есть функция JsonViews.

Вам нужен класс для представлений

public class Views {

    public static class Normal{}

    public static class Extended extends Normal{}

}

Затем вы аннотируете класс Department

import com.fasterxml.jackson.annotation.JsonView;

import java.util.List;

public class Department {

    @JsonView(Views.Normal.class)
    private Person head;

    @JsonView(Views.Normal.class)
    private Person deputy;

    @JsonView(Views.Extended.class)
    private List<Person> staff;

    public Person getHead() {
        return head;
    }

    public void setHead(Person head) {
        this.head = head;
    }

    public Person getDeputy() {
        return deputy;
    }

    public void setDeputy(Person deputy) {
        this.deputy = deputy;
    }

    public List<Person> getStaff() {
        return staff;
    }

    public void setStaff(List<Person> staff) {
        this.staff = staff;
    }
}

Сохраните класс Person как есть

public class Person {
    private String name;
    private int code;

    public Person(String name, int code) {
        this.name = name;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

В основной функции, где вы сериализуете объект, вам необходимо включить соответствующие представления.

public class Main {


    static Department createDepartment(){
        Department department = new Department();
        Person head = new Person("John", 123);
        Person deputy = new Person("Jack", 234);
        List<Person> personList = Arrays.asList(new Person("Tom", 345), new Person("Matt", 456));
        department.setHead(head);
        department.setDeputy(deputy);
        department.setStaff(personList);
        return department;

    }

    public static void main(String[] args) throws JsonProcessingException {

        Department department = createDepartment();

        ObjectMapper mapper = new ObjectMapper();

        String normal = mapper.writerWithView(Views.Normal.class).writeValueAsString(department);
        String extended = mapper.writerWithView(Views.Extended.class).writeValueAsString(department);

        System.out.println("Normal View - " + normal);
        System.out.println("Extended View - " + extended);
   }
}

Результат следующим образом:

Normal View - {"head":{"name":"John","code":123},"deputy":{"name":"Jack","code":234}}
Extended View - {"head":{"name":"John","code":123},"deputy":{"name":"Jack","code":234},"staff":[{"name":"Tom","code":345},{"name":"Matt","code":456}]}
...