杰克逊 YAML 序列化对象数组格式



我正在尝试以某种方式格式化我的杰克逊Yaml序列化。

employees:
- name: John
age: 26
- name: Bill
age: 17

但是,当我序列化对象时,这就是我得到的格式。

employees:
-
name: John
age: 26
-
name: Bill
age: 17

有没有办法去掉数组中对象开头的换行符?这纯粹是个人偏好/人类可读性问题。

这些是我目前正在 YAMLFactory 上设置的属性:

YAMLFactory yamlFactory = new YAMLFactory()
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES) //removes quotes from strings
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)//gets rid of -- at the start of the file.
.enable(YAMLGenerator.Feature.INDENT_ARRAYS);// enables indentation.

我在 Java 文档中查找了 Jackson 中的 YAMLGenerator,并查看了有关 stackoverflow 的其他问题,但我找不到执行我正在尝试做的事情的选项。

我已经尝试了CANONICAL_OUTPUT,SPLIT_LINES和LITERAL_BLOCK_STYLE属性,最后一个在设置MINIMIZE_QUOTES时自动设置。 CANONICAL_OUTPUT似乎在数组周围添加了括号。 SPLIT_LINES和LITERAL_BLOCK_STYLE与如何处理多行字符串有关。

简短的回答是,目前没有办法通过杰克逊做到这一点。这是由于 snakeyaml 中的一个错误,如果您设置 indicatorIndent 属性,则空格处理不正确,因此 snakeyaml 会添加新行。

我找到了直接使用 snakeyaml 的解决方法。

//The representer allows us to ignore null properties, and to leave off the class definitions
Representer representer = new Representer() {
//ignore null properties
@Override
protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) {
// if value of property is null, ignore it.
if (propertyValue == null) {
return null;
}
else {
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
}
}
//Don't print the class definition
@Override
protected MappingNode representJavaBean(Set<Property> properties, Object javaBean) {
if (!classTags.containsKey(javaBean.getClass())){
addClassTag(javaBean.getClass(), Tag.MAP);
}
return super.representJavaBean(properties, javaBean);
}
};

DumperOptions dumperOptions = new DumperOptions();
//prints the yaml as nested blocks
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
//indicatorIndent indents the '-' character for lists
dumperOptions.setIndicatorIndent(2);
//This is the workaround. Indent must be set to at least 2 higher than the indicator indent because of how whitespace is handled.
//If not set to 2 higher, then the newline is added.
dumperOptions.setIndent(4);
Yaml yaml = new Yaml(representer, dumperOptions);
//prints the object to a yaml string.
yaml.dump(object);

解决方法发生在在转储程序选项上设置缩进属性。您需要将缩进设置为至少比 indicatorIndent 高 2 的值,否则将添加换行符。这是由于在 snakeyaml 中如何处理空格。

查看杰克逊源代码,snakeyaml dumper 选项的创建方式如下: 这对你来说@Andrey好看?

protected DumperOptions buildDumperOptions(int jsonFeatures, int yamlFeatures,
org.yaml.snakeyaml.DumperOptions.Version version)
{
DumperOptions opt = new DumperOptions();
// would we want canonical?
if (Feature.CANONICAL_OUTPUT.enabledIn(_formatFeatures)) {
opt.setCanonical(true);
} else {
opt.setCanonical(false);
// if not, MUST specify flow styles
opt.setDefaultFlowStyle(FlowStyle.BLOCK);
}
// split-lines for text blocks?
opt.setSplitLines(Feature.SPLIT_LINES.enabledIn(_formatFeatures));
// array indentation?
if (Feature.INDENT_ARRAYS.enabledIn(_formatFeatures)) {
// But, wrt [dataformats-text#34]: need to set both to diff values to work around bug
// (otherwise indentation level is "invisible". Note that this should NOT be necessary
// but is needed up to at least SnakeYAML 1.18.
// Also looks like all kinds of values do work, except for both being 2... weird.
opt.setIndicatorIndent(1);
opt.setIndent(2);
}
// 14-May-2018: [dataformats-text#84] allow use of platform linefeed
if (Feature.USE_PLATFORM_LINE_BREAKS.enabledIn(_formatFeatures)) {
opt.setLineBreak(DumperOptions.LineBreak.getPlatformLineBreak());
}
return opt;
}

我遇到了这个问题,最终写了这篇博客文章来描述我想出的解决方案。简而言之,我创建了一个自定义的YAMLGeneratorYAMLFactory子类,并用它来配置杰克逊的YAMLMapper。不"干净",但不大,合理有效。让我设置任意DumperOption

来源如下,但也可在此要点中找到。

警告 - 我在Kotlin中完成了这一切,但它是微不足道的代码,应该很容易向后移植到 Java:

val mapper: YAMLMapper = YAMLMapper(MyYAMLFactory()).apply {
registerModule(KotlinModule())
setSerializationInclusion(JsonInclude.Include.NON_EMPTY)
}

class MyYAMLGenerator(
ctx: IOContext,
jsonFeatures: Int,
yamlFeatures: Int,
codec: ObjectCodec,
out: Writer,
version: DumperOptions.Version?
): YAMLGenerator(ctx, jsonFeatures, yamlFeatures, codec, out, version) {
override fun buildDumperOptions(
jsonFeatures: Int,
yamlFeatures: Int,
version: DumperOptions.Version?
): DumperOptions {
return super.buildDumperOptions(jsonFeatures, yamlFeatures, version).apply {
//
// NOTE: CONFIGURATION HAPPENS HERE!!
//
defaultScalarStyle = ScalarStyle.LITERAL;
defaultFlowStyle = FlowStyle.BLOCK
indicatorIndent = 2
nonPrintableStyle = ESCAPE
indent = 4
isPrettyFlow = true
width = 100
this.version = version
}
}
}
class MyYAMLFactory(): YAMLFactory() {
@Throws(IOException::class)
override fun _createGenerator(out: Writer, ctxt: IOContext): YAMLGenerator {
val feats = _yamlGeneratorFeatures
return MyYAMLGenerator(ctxt, _generatorFeatures, feats,_objectCodec, out, _version)
}
}

在当今的环境中,由于引入了YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR,因此至少使用jackson-dataformat-yaml版本2.12.x,您可以简单地执行以下操作:

public ObjectMapper yamlObjectMapper() {
final YAMLFactory factory = new YAMLFactory()
.enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR)
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
.disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
return new ObjectMapper(factory);
}

这将为您提供以下输出:

employees:
- name: John
age: 26
- name: Bill
age: 17

最新更新