java - Jackson - 如何获得依赖于 View 的 CsvSchema?

标签 java csv jackson

我正在尝试将我的 POJO 转换为 2 种不同的 CSV 表示形式。

我的 POJO:

@NoArgsConstructor
@AllArgsConstructor
public static class Example {
    @JsonView(View.Public.class)
    private String a;
    @JsonView(View.Public.class)
    private String b;
    @JsonView(View.Internal.class)
    private String c;
    @JsonView(View.Internal.class)
    private String d;

    public static final class View {
        interface Public {}
        interface Internal extends Public {}
    }
}

Public View 公开字段 abInternal View 公开所有字段。

问题是,如果我用 .writerWithSchemaFor(Example.class) 构造 ObjectWriter,我的所有字段都包含但忽略了 View 定义的内容。 ObjectWriter 将创建由 Example.class 定义的模式,但如果我应用 .withView 它只会隐藏字段,不会忽略它们。

这意味着我必须手动构建架构。

测试:

@Test
public void testJson() throws JsonProcessingException {
    final ObjectMapper mapper = new ObjectMapper();

    final Example example = new Example("1", "2", "3", "4");
    final String result = mapper.writerWithView(Example.View.Public.class).writeValueAsString(example);
    System.out.println(result); // {"a":"1","b":"2"}
}

@Test
public void testCsv() throws JsonProcessingException {
    final CsvMapper mapper = new CsvMapper();

    final Example example = new Example("1", "2", "3", "4");
    final String result = mapper.writerWithSchemaFor(Example.class).withView(Example.View.Public.class).writeValueAsString(example);
    System.out.println(result); // 1,2,,
}

@Test
public void testCsvWithCustomSchema() throws JsonProcessingException {
    final CsvMapper mapper = new CsvMapper();

    CsvSchema schema = CsvSchema.builder()
            .addColumn("a")
            .addColumn("b")
            .build();

    final Example example = new Example("1", "2", "3", "4");
    final String result = mapper.writer().with(schema).withView(Example.View.Public.class).writeValueAsString(example);
    System.out.println(result); // 1,2
}

testCsv 测试有 4 个字段,但排除了 2 个。 testCsvWithCustomSchema 测试只有我想要的字段。

有没有办法获得与我的 @JsonView 匹配的 CsvSchema 而无需自己构建它?

最佳答案

这是我用反射做的一个解决方案,我对它不是很满意,因为它仍然是“手动”构建模式。

这个解决方案也很糟糕,因为它忽略了像 MapperFeature.DEFAULT_VIEW_INCLUSION 这样的映射器配置。

这似乎是在做一些图书馆应该已经提供的事情。

@AllArgsConstructor
public class GenericPojoCsvSchemaBuilder {

    public CsvSchema build(final Class<?> type) {
        return build(type, null);
    }

    public CsvSchema build(final Class<?> type, final Class<?> view) {
        return build(CsvSchema.builder(), type, view);
    }

    public CsvSchema build(final CsvSchema.Builder builder, final Class<?> type) {
        return build(builder, type, null);
    }

    public CsvSchema build(final CsvSchema.Builder builder, final Class<?> type, final Class<?> view) {
        final JsonPropertyOrder propertyOrder = type.getAnnotation(JsonPropertyOrder.class);

        final List<Field> fieldsForView;

        // DO NOT use Arrays.asList because it uses an internal fixed length implementation which cannot use .removeAll (throws UnsupportedOperationException)
        final List<Field> unorderedFields = Arrays.stream(type.getDeclaredFields()).collect(Collectors.toList());

        if (propertyOrder != null && propertyOrder.value().length > 0) {
            final List<Field> orderedFields = Arrays.stream(propertyOrder.value()).map(s -> {
                try {
                    return type.getDeclaredField(s);
                } catch (final NoSuchFieldException e) {
                    throw new IllegalArgumentException(e);
                }
            }).collect(Collectors.toList());

            if (propertyOrder.value().length < type.getDeclaredFields().length) {
                unorderedFields.removeAll(orderedFields);
                orderedFields.addAll(unorderedFields);
            }

            fieldsForView = getJsonViewFields(orderedFields, view);
        } else {
            fieldsForView = getJsonViewFields(unorderedFields ,view);
        }

        final JsonIgnoreFieldFilter ignoreFieldFilter = new JsonIgnoreFieldFilter(type.getDeclaredAnnotation(JsonIgnoreProperties.class));

        fieldsForView.forEach(field -> {
            if (ignoreFieldFilter.matches(field)) {
                builder.addColumn(field.getName());
            }
        });

        return builder.build();
    }

    private List<Field> getJsonViewFields(final List<Field> fields, final Class<?> view) {
        if (view == null) {
            return fields;
        }

        return fields.stream()
                .filter(field -> {
                    final JsonView jsonView = field.getAnnotation(JsonView.class);
                    return jsonView != null && Arrays.stream(jsonView.value()).anyMatch(candidate -> candidate.isAssignableFrom(view));
                })
                .collect(Collectors.toList());
    }

    private class JsonIgnoreFieldFilter implements ReflectionUtils.FieldFilter {

        private final List<String> fieldNames;

        public JsonIgnoreFieldFilter(final JsonIgnoreProperties jsonIgnoreProperties) {
            if (jsonIgnoreProperties != null) {
                fieldNames = Arrays.asList(jsonIgnoreProperties.value());
            } else {
                fieldNames = null;
            }
        }

        @Override
        public boolean matches(final Field field) {
            if (fieldNames !=  null && fieldNames.contains(field.getName())) {
                return false;
            }

            final JsonIgnore jsonIgnore = field.getDeclaredAnnotation(JsonIgnore.class);
            return jsonIgnore == null || !jsonIgnore.value();
        }
    }
}

关于java - Jackson - 如何获得依赖于 View 的 CsvSchema?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45962482/

相关文章:

scala - 使用 Jackson 对 Scala 枚举进行 JSON 序列化

java - 使用 spring boot 将 JSON 反序列化为具有通用对象列表的 POJO

java - 努力简化 HashMap 对象放置

java - HashSet 与 ArrayList contains() 几个小型 String 集合的性能

Java堆栈接口(interface): IndexOutOfBounds

Linux - 将多个 CSV 文件合并为一个

python - 在 Python 中将变量更改为输出文件的名称

java - 如何使程序在不重置的情况下使用不同的变量?

javascript - 在 d3.js 中使用 CSV 而不是 TSV 数据

java - JacksonFeature 打破了 JsonIgnoreProperties