我该如何使用@JsonTypeInfo和@JsonSubTypes来实例化具有不同配置的类?

5
我希望创建一个配置文件,允许我定义不同的数据生成器,每个生成器都需要不同的配置。但是,它们都共享相同的方法generateRow,因此这些类可以实现一个接口。我正在使用Jackson版本2.9.4。
为了说明,这里有两个示例配置文件:
{
    "data": {
        "type": "standard",
        "config": {
            "rows": 1000,
            "columns": 10
        }
    }
}

并且

{
    "data": {
        "type": "totalSize",
        "config": {
            "sizeInBytes": 1073741824,
            "cellDensityInBytes": 12,
            "columns": 5
        }
    }
}

第一个数据生成器会简单地创建一个给定行和列数的文件,而第二个生成器则会创建一个预定义大小的文件,确定需要满足配置变量(即列数和单元格密度)的行数。

因此,我创建了一个接口:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = IGenerateRows.PROPERTY, defaultImpl = StandardRowGenerator.class)
@JsonSubTypes(value = { @Type(StandardRowGenerator.class) })
public interface IGenerateRows {

    public static final String PROPERTY = "type";

    public String[] generateRow();
}

我至少有一个具体的实现:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;

@JsonTypeName(value = StandardRowGenerator.TYPE)
public class StandardRowGenerator {

    public static final String TYPE = "standard";

    private static final String ROWS = "rows";
    private static final String COLUMNS = "columns";

    @JsonProperty(value = ROWS, required = true)
    private int rows;

    @JsonProperty(value = COLUMNS, required = true)
    private int columns;
}

我无法理解如何处理配置文件中数据生成器节点的config节点。我该如何正确设置我的具体类来定义它们需要生成数据的属性?

在我的引导代码中,我按照以下方式实例化整个配置对象:

new ObjectMapper().readValue(inputStream, DataGeneratorConfig.class);

为了简洁起见,我省略了getter和setter以及其余与问题无关的配置文件。如果需要更多细节或代码,请告诉我。

1个回答

4

我对你的类的底层实现以及它们所生成的数据有些不确定。

但你朝着正确的方向前进,我已经将我认为符合你要求的一个工作示例推送到了这个存储库中,请注意,这里使用了https://projectlombok.org/来生成POJO,因为我很懒。

https://github.com/Flaw101/jackson-type-info

  • 它将忽略 "data" 节点。这主要是因为我太懒了,可以将实体包装在 Data 类中进行处理。测试中的 ObjectMapper 启用了此项功能所需的功能。
  • 它将读取/写入配置类的数据。与您指定的示例相一致。
  • 并没有什么捷径可以自动反序列化数据。您可能只能将其写入 map->Object 中,但那非常混乱,并且像 lombok / IDE 类生成这样的工具使得创建这些实体应该只需要几秒钟的时间。

IGenerateRow 看起来像这样:

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = RowGenerator.PROPERTY, defaultImpl = StandardRowGenerator.class)
@JsonSubTypes(value = { @Type(StandardRowGenerator.class), @Type(TotalSizeGeneartor.class) })
@JsonRootName(value = "data")
public abstract interface RowGenerator {

    public static final String PROPERTY = "type";

    Config getConfig();
}

而Config只是具体实现的标记接口。

public interface Config {



}

简单类型生成器现在变成了:
@JsonTypeName(value = StandardRowGenerator.TYPE)
@Data

public class StandardRowGenerator implements RowGenerator {

    public static final String TYPE = "standard";

    private StandardConfig config;

    @Data
    public static class StandardConfig implements Config {
        private int rows;
        private int columns;
    }
}

同样适用于TotalSize

@JsonTypeName(value = TotalSizeGeneartor.TYPE)
@Data
public class TotalSizeGeneartor implements RowGenerator {

    public static final String TYPE = "totalSize";

    private TotalSizeConfig config;

    @Data
    public static class TotalSizeConfig implements Config {
        private long sizeInBytes;
        private int cellDensityInBytes;
        private int columns;
    }
}

这些内容可以通过更多/更好的通用类型信息来改进,以便能够获取到有关配置的具体引用。

测试类在资源文件夹中读取您的两个配置文件,将它们写入对象并返回字符串,比较前后的结果,确保没有空属性,接口实现正确。

请注意,此处使用了AssertJassertThat

public class JacksonTest {

    private ObjectMapper mapper;
    private String json;

    @Before
    public void setup() throws Exception {
        mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
        mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
    }

    @Test
    public void testDeserStandard() throws Exception {
        json = StringUtils.deleteWhitespace(
                new String(Files.readAllBytes(Paths.get("src/main/resources/standard.json")), StandardCharsets.UTF_8));

        RowGenerator generator = mapper.readValue(json, RowGenerator.class);
        assertThat(generator).hasNoNullFieldsOrProperties().isExactlyInstanceOf(StandardRowGenerator.class);
        assertThat(generator.getConfig()).hasNoNullFieldsOrProperties().isExactlyInstanceOf(StandardConfig.class);
        assertThat(json).isEqualTo(mapper.writeValueAsString(generator));
        System.out.println(generator);
    }

    @Test
    public void testDeserTotalsize() throws Exception {
        json = StringUtils.deleteWhitespace(
                new String(Files.readAllBytes(Paths.get("src/main/resources/totalsize.json")), StandardCharsets.UTF_8));

        RowGenerator generator = mapper.readValue(json, RowGenerator.class);
        assertThat(generator).hasNoNullFieldsOrProperties().isExactlyInstanceOf(TotalSizeGeneartor.class);
        assertThat(generator.getConfig()).hasNoNullFieldsOrProperties().isExactlyInstanceOf(TotalSizeConfig.class);
        assertThat(json).isEqualTo(mapper.writeValueAsString(generator));
        System.out.println(generator);

    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接