【问题标题】:How can I specify certain field to be serialized into JSON using Jackson?如何使用 Jackson 指定要序列化为 JSON 的某些字段?
【发布时间】:2019-06-21 22:16:09
【问题描述】:

我有两个类 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,而不是获取整个对象表示。

所以,我发现我需要在 Injury 类上应用实现 StdSerializer 的自定义 Serializer。所以这就是我到目前为止得到的:

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) 你能推荐我读一些关于杰克逊的东西吗?因为此时我脑子里有点乱?

感谢您的耐心等待,期待您的回复:)

【问题讨论】:

标签: java json jackson


【解决方案1】:

您可以为此目的使用数据传输对象 (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 用于不同的目的。

【讨论】:

  • 谢谢,我会用这个方法。
【解决方案2】:

您可能会发现使用 @JsonIgnore 注释而不是编写自定义序列化程序更简单。举个例子

public class Person {

  private int id;

  @JsonIgnore
  private String first;

  @JsonIgnore
  private String last;

  @JsonIgnore
  private int age;

  // getters and setters omitted
}

当 Jackson 序列化这个类时,它只在生成的 JSON 中包含“id”属性。

  @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);
  }

【讨论】:

  • 如果我用以下样式注释运动员类,我将丢失运动员实体所需的所有字段。当我使用“/athletes/1” URL 时,我需要运动员类的完整表示,而当我使用“athletes/1/injuries” URL 时,我只需要 id 值。
  • 我明白了。在这种情况下,我同意@kimreik 的答案stackoverflow.com/a/56713883 是您的最佳选择:使用不同的数据模型。
【解决方案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'

如果你可以用正则表达式替换会更干净。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多