У меня есть два класса 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) Не могли бы вы порекомендовать мне что-нибудь почитать о Джексоне, потому что на данный момент у меня немного каша в голове по этому поводу?
Спасибо за терпение, жду ваших ответов :)
Да, я проверял это раньше, но проблема в том, что если я отмечу поле Athlete как @JsonIgnore в классе Injury, то я больше не смогу использовать это поле, поэтому сериализация всего объекта спортсмена в классе Injury будет заблокировано -> это не то, что я хочу, я хочу, чтобы в свойстве Athlete был только один сериализованный атрибут -> просто поле id.
Может это поможет stackoverflow.com/questions/25893985/…




Вы можете попробовать манипулировать строкой 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».
Понимаю. В этом случае я согласен с тем, что ответ @kimreik stackoverflow.com/a/56713883 - ваш лучший вариант: используйте другую модель данных.
Для этих целей вы можете использовать объект передачи данных (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 для разных целей.
Спасибо, воспользуюсь этим подходом.
Вы можете использовать свойство "JsonIgnore", вот дополнительная информация о: документы Джексона. Я рекомендую вам следовать учебникам baeldung, вот несколько полезных ссылок: 1. baeldung.com/jackson 2. baeldung.com/jackson-ignore-properties-on-serialization