我正在尝试将我的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查看显示的字段ab,而Internal视图显示所有字段。

问题是,如果我用ObjectWriter构造.writerWithSchemaFor(Example.class),则所有字段都包括在内,但被视图定义忽略。 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测试只有我想要的字段。

有没有一种方法可以获取与我的CsvSchema匹配的@JsonView而不需要自己构造它?

最佳答案

这是我反思的一种解决方案,我对此并不满意,因为它仍在“手动”构建架构。

该解决方案也很糟糕,因为它忽略了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/

10-11 10:38