Как я могу указать определенное поле для сериализации в JSON с помощью Джексона?

У меня есть два класса Athlete и Injury, последний содержит объект Athlete, когда происходит сериализация, я возвращаю следующее представление JSON: {"id":X,"kindOfInjury":"...","muscle":"...","side":"...","outOfTrainig":Y,"injuryDate":"2018-Jun-02","athlete":{"id":X,"firstName":"...","lastName":"...","age":X,"email":"..."}}

Я не хочу получать всю информацию об Athlete — просто значение id, например "athleteId":1, вместо того, чтобы получать полное представление объекта.

Итак, я обнаружил, что мне нужно применить свой пользовательский сериализатор, который реализует StdSerializer для класса Injury. Итак, вот что я получил до сих пор:

class InjurySerializer extends StdSerializer<Injury> {

    public InjurySerializer() {
        this(null);
    }

    public InjurySerializer(Class<Injury> i) {
        super(i);
    }

    @Override
    public void serialize(
            Injury value, JsonGenerator jgen, SerializerProvider provider)
            throws IOException, JsonProcessingException {

        jgen.writeStartObject();
        jgen.writeNumberField("id", value.getId());
        jgen.writeStringField("kindOfInjury", value.getKindOfInjury());
        jgen.writeStringField("muscle", value.getMuscle());
        jgen.writeStringField("side", value.getSide());
        jgen.writeNumberField("outOfTraining", value.getOutOfTraining());
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MMM-dd");
        Date date = new Date();
        String ourformat = formatter.format(date.getTime());
        jgen.writeStringField("injuryDate", ourformat);
        jgen.writeNumberField("athleteId", value.getAthlete().getId());
        jgen.writeEndObject();
    }
}

И собственно класс травм:

@Entity
@Table(name = "INJURY")
@JsonSerialize(using = InjurySerializer.class)
public class Injury {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "INJURY_ID")
    private Long id;

    @Column(name = "KIND_OF_INJURY")
    private String kindOfInjury;

    @Column(name = "MUSCLE")
    private String muscle;

    @Column(name = "SIDE")
    private String side;

    @Column(name = "OUT_OF_TRAINING")
    private Integer outOfTraining;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MMM-dd")
    @Column(name = "INJURY_DATE")
    private Date injuryDate;

    @ManyToOne
    @JoinColumn(name = "ATHLETE_ID")
    private Athlete athlete;

Итак, это решение работает, но выглядит ужасно...

Вопрос следующий: 1) Есть ли какой-либо механизм, который предоставляет мне функциональность для изменения сериализации только ОДНОГО свойства, которое мне действительно нужно, вместо того, чтобы писать весь этот утомительный код, где фактическое изменение находится только в этой строке? :

jgen.writeNumberField("athleteId", value.getAthlete().getId());

2) Не могли бы вы порекомендовать мне что-нибудь почитать о Джексоне, потому что на данный момент у меня немного каша в голове по этому поводу?

Спасибо за терпение, жду ваших ответов :)

Вы можете использовать свойство "JsonIgnore", вот дополнительная информация о: документы Джексона. Я рекомендую вам следовать учебникам baeldung, вот несколько полезных ссылок: 1. baeldung.com/jackson 2. baeldung.com/jackson-ignore-properties-on-serialization

Ricardo Brasil 22.06.2019 00:35

Да, я проверял это раньше, но проблема в том, что если я отмечу поле Athlete как @JsonIgnore в классе Injury, то я больше не смогу использовать это поле, поэтому сериализация всего объекта спортсмена в классе Injury будет заблокировано -> это не то, что я хочу, я хочу, чтобы в свойстве Athlete был только один сериализованный атрибут -> просто поле id.

Maksim 22.06.2019 00:53

Может это поможет stackoverflow.com/questions/25893985/…

Ricardo Brasil 22.06.2019 01:24
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
3
61
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете попробовать манипулировать строкой json, используя базовый метод замены строки. Я запустил ваш json и преобразовал его в нужный формат:

public static void main(String args[]) {
    String json = "{\"id\":123,\"kindOfInjury\":\"...\",\"muscle\":\"...\",\"side\":\"...\",\"outOfTrainig\":Y,\"injuryDate\":\"2018-Jun-02\",\"athlete\":{\"id\":456,\"firstName\":\"...\",\"lastName\":\"...\",\"age\":14,\"email\":\"...\"}}";
    JsonObject injury = new JsonParser().parse(json).getAsJsonObject();
    JsonObject athelete = new JsonParser().parse(injury.get("athlete").toString()).getAsJsonObject();
    String updateJson = injury.toString().replace(injury.get("athlete").toString(), athelete.get("id").toString());
    updateJson = updateJson.replace("athlete", "athleteId");
    System.out.println(updateJson);
}

выход:

{"id":123,"kindOfInjury":"...","muscle":"...","side":"...","outOfTrainig":"Y","injuryDate":"2018-Jun-02","athleteId":456}

Зависимость:

implementation 'com.google.code.gson:gson:2.8.5'

Если вы можете заменить регулярным выражением, это будет немного чище.

Возможно, вам будет менее утомительно использовать аннотацию @JsonIgnore вместо написания собственного сериализатора. Возьмите этот пример

public class Person {

  private int id;

  @JsonIgnore
  private String first;

  @JsonIgnore
  private String last;

  @JsonIgnore
  private int age;

  // getters and setters omitted
}

Когда Джексон сериализует этот класс, он включает только свойство «id» в результирующий JSON.

  @Test
  void serialize_only_includes_id() throws JsonProcessingException {
    final var person = new Person();
    person.setId(1);
    person.setFirst("John");
    person.setLast("Smith");
    person.setAge(22);

    final var mapper = new ObjectMapper();
    final var json = mapper.writeValueAsString(person);
    assertEquals("{\"id\":1}", json);
  }

Если я аннотирую класс спортсмена в следующем стиле, я потеряю все поля, необходимые для сущности спортсмена. Мне нужно полное представление класса Athlete, когда я использую URL-адрес «/athletes/1», и только значение идентификатора, когда я использую URL-адрес «athletes/1/injuries».

Maksim 22.06.2019 22:13

Понимаю. В этом случае я согласен с тем, что ответ @kimreik stackoverflow.com/a/56713883 - ваш лучший вариант: используйте другую модель данных.

jdgilday 27.06.2019 03:14
Ответ принят как подходящий

Для этих целей вы можете использовать объект передачи данных (DTO).

Создайте простой POJO следующим образом:

public class InjuryDTO {

  //all other required fields from Injury model...

  @JsonProperty("athlete_id")
  private Long athleteId;
}

И конвертер для него:

@Component
public class InjuryToDTOConverter{

  public InjuryDTO convert(Injury source){
    InjuryDTO target = new InjuryDTO();
    BeanUtils.copyProperties(source, target); //it will copy fields with the same names
    target.setAthleteId(source.getAthlete().getId());
    return target;
  }
}

Вы можете использовать его так:

@RestController("/injuries")
public class InjuryController {

  @Autowired
  private InjuryToDTOConverter converter;

  @Autowired
  private InjuryService injuryService;

  @GetMapping
  public InjuryDTO getInjury(){
    Injury injury = injuryService.getInjury();
    return converter.convert(injury);
  }
}

Преимущество этого подхода в том, что вы можете иметь несколько DTO для разных целей.

Спасибо, воспользуюсь этим подходом.

Maksim 22.06.2019 22:14

Другие вопросы по теме