3

我用来Jackson将 s 序列POJO化为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的嵌套类,并且文档指出这不适用于自定义序列化程序(不知道这在这里会有所不同)。

4

2 回答 2

1

您可以尝试使用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,1,123
124,Jerry,2,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()));

使用JsonAnyGetterwithCSV真的很棘手,将它与其他注释混合可能会出现问题,请查看:能否添加 JsonAnyGetter 和 JsonAnySetter 注释支持?

于 2021-01-12T16:58:57.967 回答
1

好吧,我以前从未见过这个,如果这个网站上有人知道怎么做,我会很高兴的。

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

例如:

  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, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("myValue", value.getMyValue());
        gen.writeStringField("my_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, JsonGenerator gen, 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();
    }
}
于 2021-01-11T15:46:23.133 回答