微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Jackson 如何将一个 Pojo 字段映射到 2 个json字段相同的内容,不同的名称

如何解决Jackson 如何将一个 Pojo 字段映射到 2 个json字段相同的内容,不同的名称

我使用 JacksonPOJO 序列化为 CSV。现在我们需要将某些字段的命名更改为 snake_case。这很容易通过 @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 完成。

出于兼容性原因,我们需要一些重命名的字段以及它们的旧名称

例如:

public class Pojo {
    private int someField;
}

认将序列化为“someField”,SnakeCaseStrategy 将序列化为“some_field”。

如何同时获得序列化?:

{
  "someField" : "one","some_field" : "one" 
}

我的第一次尝试是混合:

public abstract class PojoFormat {

    @JsonProperty("someField")
    abstract String getSomeField();

}

但这实际上只会撤消命名策略更改。 那么如何在序列化中复制字段 - 最好不要通过更改 Pojo(当所有客户端都可以处理时,应删除此复制的字段)。

小更新:

在我的真实课堂中有一些使用 JsonUnwrapped 的嵌套类,并且文档指出这不适用于自定义序列化程序(不知道这在这里有什么不同)。

解决方法

嗯,我以前从未见过这个,如果本网站有人知道如何,我会很高兴。

在我看来,最简单的方法是使用自定义序列化程序。

例如:

  1. 使用@JsonSerialize 注释
  2. 注册一个模块
  3. 带反射的动态序列化器

@JsonSerialize 注解

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

@JsonSerializer(using=PojoSerializer.class)
class Pojo {
 
  private String myValue;
   
  // getters and setters

}

class PojoSerializer extends StdSerializer<Pojo> {

  public PojoSerializer() {
   super(Pojo.class);
  }

      @Override
    public void serialize(Pojo value,JsonGenerator gen,SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("myValue",value.getMyValue());
        gen.writeStringField("my_value",value.getMyValue());
        gen.writeEndObject();

    }
}

模块

static class Pojo {

    private String myValue;

    public String getMyValue() {
        return myValue;
    }

    public Pojo setMyValue(String myValue) {
        this.myValue = myValue;
        return this;
    }
}

static class PojoSerializer extends StdSerializer<Pojo> {

    public PojoSerializer() {
        super(Pojo.class);
    }

    @Override
    public void serialize(Pojo value,value.getMyValue());
        gen.writeEndObject();
    }
}


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

    final ObjectMapper mapper = new ObjectMapper();

    final SimpleModule module = new SimpleModule("PojoModule");

    module.addSerializer(Pojo.class,new PojoSerializer());

    mapper.registerModule(module);

    final Pojo pojo = new Pojo();
    pojo.setMyValue("This is the value of my pojo");

    System.out.println(mapper.writeValueAsString(pojo));

}

反思

我为你写了一些代码,你可能想看看以获得新的想法。 这是一种通用方式(只是为了不编写多个序列化程序)。

// The serializer will be register in the ObjectMapper module.
static class Pojo {

    private String myValue = "With snake and camel";
    private String value = "Without snake case";
    private String thirdValue = "snake & camel";

}

// using the annotation
@JsonSerialize(using = PojoSerializer.class)
static class Pojo2 {

    private String pojoName = "Pojo 2";
    private String pojo = "pojp";

}

static class PojoSerializer extends StdSerializer<Object> {

    public PojoSerializer() {
        super(Object.class);
    }

    @Override
    public void serialize(Object value,SerializerProvider provider) throws IOException {
        gen.writeStartObject();

        final Field[] fields = value.getClass().getDeclaredFields();
        for(final Field field : fields) {

            final String name = field.getName();
            final String fieldValue;
            try {
                // Do not use this!
                fieldValue = (String)field.get(value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }

            byte firstUpperCase = -1;
            for(byte index = 0; index < name.length(); index++) {
                final char caractere = name.charAt(index);

                // A ascii code is 66 decimal,and 90 is the Z in decimal
                if(caractere > 'A' && caractere < 'Z') {
                    // found the first upper
                    firstUpperCase = index;
                    break;
                }
            }

            // writes the normal field name
            gen.writeStringField(name,fieldValue);

            // if the name is in camel case,we will write in snake case too.
            if(firstUpperCase != -1) {
                final char lowerLetter = (char)((int) name.charAt(firstUpperCase) + 32);
                final String left = name.substring(0,firstUpperCase);
                final String right = String.format("%c%s",lowerLetter,name.substring(firstUpperCase + 1));
                gen.writeStringField(String.format("%s_%s",left,right),fieldValue);

            }
        }
        gen.writeEndObject();
    }
}
,

您可以尝试使用 JsonAnyGetter 注释并为每个 POJO 额外映射定义以实现向后兼容性。

让我们创建一个简单的界面:

interface CompatibleToVer1 {

    @JsonAnyGetter
    Map<String,Object> getCompatibilityView();
}

和两个实现它的类:

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class RootPojo implements CompatibleToVer1 {
    private int rootId;

    @JsonUnwrapped
    private SomePojo pojo;

    @Override
    public Map<String,Object> getCompatibilityView() {
        return Collections.singletonMap("rootId",rootId);
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
class SomePojo implements CompatibleToVer1 {

    private int someField;
    private String someName;

    @Override
    public Map<String,Object> getCompatibilityView() {
        Map<String,Object> extra = new LinkedHashMap<>();
        extra.put("someField",someField);

        return extra;
    }
}

如您所见,我使用自定义名称为每个 POJO 定义了额外的列。序列化到 JSON 很简单:

ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);

SomePojo pojo = new SomePojo(123,"Tom");
mapper.writeValue(System.out,new RootPojo(1,pojo));

以上代码打印:

{
  "root_id" : 1,"some_field" : 123,"some_name" : "Tom","someField" : 123,"rootId" : 1
}

但是对于 CSV,我们需要创建额外的配置:

CsvMapper csvMapper = CsvMapper.builder().build();

CsvSchema pojoExtraScheme = CsvSchema.builder()
        .addColumn("someField")
        .build();
CsvSchema rootExtraScheme = CsvSchema.builder()
        .addColumn("rootId")
        .build();
CsvSchema compatibleSchema = CsvSchema.emptySchema()
        .withHeader()
        .withColumnsFrom(csvMapper.schemaFor(RootPojo.class))
        .withColumnsFrom(rootExtraScheme)
        .withColumnsFrom(csvMapper.schemaFor(SomePojo.class))
        .withColumnsFrom(pojoExtraScheme);


SomePojo tom = new SomePojo(123,"Tom");
SomePojo jerry = new SomePojo(124,"Jerry");
List<RootPojo> pojos = Arrays.asList(new RootPojo(1,tom),new RootPojo(2,jerry));
ObjectWriter writer = csvMapper.writer(compatibleSchema);
System.out.println(writer.writeValueAsString(pojos));

以上代码打印:

some_field,some_name,root_id,rootId,someField
123,Tom,1,123
124,Jerry,2,124

如果你不想指定额外的列两次,你可以基于我们的接口实现 builder 方法:

CsvSchema createSchemaFor(CompatibleToVer1 entity) {
    CsvSchema.Builder builder = CsvSchema.builder();
    entity.getCompatibilityView().keySet().forEach(builder::addColumn);

    return builder.build();
}

并使用如下:

CsvSchema compatibleSchema = CsvSchema.emptySchema()
        .withHeader()
        .withColumnsFrom(csvMapper.schemaFor(RootPojo.class))
        .withColumnsFrom(createSchemaFor(new RootPojo()))
        .withColumnsFrom(csvMapper.schemaFor(SomePojo.class))
        .withColumnsFrom(createSchemaFor(new SomePojo()));

JsonAnyGetterCSV 一起使用非常棘手,并且将其与其他注释混合使用可能会出现问题,请查看:Could please add JsonAnyGetter and JsonAnySetter annotations support?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。