【问题标题】:Jackson YAML Serialization Object Arrays FormatJackson YAML 序列化对象数组格式
【发布时间】:2019-03-17 22:56:19
【问题描述】:

我正在尝试以某种方式格式化我的 Jackson 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.

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

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

【问题讨论】:

  • 由于 Jackson 使用 SnakeYAML 进行 YAML 处理,因此需要在 SnakeYAML 中配置一个选项,以便 Jackson 可以设置该选项。 afaik SnakeYAML 中没有这样的选项(查看它的 DumperConfig),所以它似乎不可能。
  • 我遇到了同样的问题。如果这只是一个可读性问题并且文件不是一遍又一遍地生成,您可以简单地在 IntelliJ Idea 中加载它并选择“代码 - 格式”,所有内容都被格式化,没有换行符。

标签: java serialization jackson yaml snakeyaml


【解决方案1】:

简短的回答是目前没有办法通过杰克逊来做到这一点。这是由于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);

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

【讨论】:

  • 您能报告 SnakeYAML 中的错误吗? (我是 SnakeYAML 开发者)
  • @Andrey 看起来像是去年为此创建了一个错误,但它被标记为无效。很高兴重新打开并解决此问题:bitbucket.org/asomov/snakeyaml/issues/416/…
  • 随意创建一个测试来修复它。
  • 我看到了同样的行为。 @Andrey 最好创建一个测试用例/存储库来证明这一点?
【解决方案2】:

查看 Jackson 源代码,snakeyaml 转储程序选项的创建方式如下: @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;
    }

【讨论】:

    【解决方案3】:

    我遇到了这个问题,最后写了这篇博文来描述我想出的解决方案。简而言之,我创建了YAMLGeneratorYAMLFactory 的自定义子类,并用它来配置Jackson 的YAMLMapper。不是“干净”,但不是很大且相当有效。让我任意设置DumperOptions

    来源如下,但also available in this gist

    警告 - 我在 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)
      }
    }
    

    【讨论】:

      【解决方案4】:

      由于YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR的引入,在今天使用至少jackson-dataformat-yaml2.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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-05
        • 2011-09-15
        相关资源
        最近更新 更多