在保留注释的同时在 java 中修改 YAML

Posted

技术标签:

【中文标题】在保留注释的同时在 java 中修改 YAML【英文标题】:Modifying YAML in java while preserving comments 【发布时间】:2018-09-23 12:29:06 【问题描述】:

我们如何修改现有的 YAML 并在其中保留 cmets。 是否有任何 Java 解析器可以做到这一点? 例如,如果我有以下 YAML:

#This is a test YAML
 name: abcd
 age: 23
#Test YAML ends here.

有没有办法可以使用 java 解析器编辑这个 Yaml 并保留 cmets。

【问题讨论】:

你吃过蛇山药吗?默认不支持吗? 【参考方案1】:

如果你使用snakeyaml,你应该修改ScannerImpl文件。

注意:将内嵌注释读取为文本

private Token scanPlain() 
        StringBuilder chunks = new StringBuilder();
        Mark startMark = reader.getMark();
        Mark endMark = startMark;
        int indent = this.indent + 1;
        String spaces = "";
        while (true) 
            int c;
            int length = 0;
            // A comment indicates the end of the scalar.
            // read the in-line comment as text
//            if (reader.peek() == '#' && ) 
//                break;
//            
            while (true) 
                c = reader.peek(length);
                if (Constant.NULL_BL_T_LINEBR.has(c)
                        || (c == ':' && Constant.NULL_BL_T_LINEBR.has(reader.peek(length + 1), flowLevel != 0 ? ",[]":""))
                        || (this.flowLevel != 0 && ",?[]".indexOf(c) != -1)) 
                    break;
                
                length++;
            
            if (length == 0) 
                break;
            
            this.allowSimpleKey = false;
            chunks.append(spaces);
            chunks.append(reader.prefixForward(length));
            endMark = reader.getMark();
            spaces = scanPlainSpaces();
            // System.out.printf("spaces[%s]\n", spaces);
            if (spaces.length() == 0
                    // read the in-line comment as text
//                    || reader.peek() == '#'
                    || (this.flowLevel == 0 && this.reader.getColumn() < indent)) 
                break;
            
        
        return new ScalarToken(chunks.toString(), startMark, endMark, true);
    

【讨论】:

【参考方案2】:

我写了一个 groovy 脚本来解决这个问题。 Java 版本非常相似:

def key = "name"
def value = "efgh"
def yamlFile = new File("file.yaml")
def yamlFileLines = new StringBuilder()
def foundKey = false
yamlFile.text.eachLine  line ->
    if (!foundKey && line.contains("$key:")) 
        line = line.replaceAll(/$key:.*/, "$key: $value")
        foundKey = true
    
    yamlFileLines.append("$line\n")

if (foundKey) 
    yamlFile.text = yamlFileLines.toString()
 else 
    throw new StopExecutionException("Could not find key '$key' in file $yamlFile.getAbsolutePath()")

【讨论】:

【参考方案3】:

截至撰写本文时,还没有适用于 Java 的往返 YAML 解析器。有著名的SnakeYAML,它不保留cmets(参见author's comment here),还有一个名为camel的较新项目,我对此知之甚少;但绝对不是往返。

理论上你可以做的是使用 SnakeYaml 的 Yaml.parse 然后遍历事件。每个事件都有一个开始和一个结束标记,给出了事件的开始和结束行和列。这使得将事件映射回源并发现源中未解析为事件的部分(可能是 cmets)成为可能。有了这个映射,您现在可以修改事件列表并将其写回。最后,您再次阅读结果,发现原始 YAML 中存在 cmets 而修改后的 YAML 中没有的事件之间的差距,然后重新插入这些 cmets,为您提供修改后的最终 YAML cmets。

当然,这是非常复杂的。我不建议这样做,除非你 a) 对 YAML 的结构有深刻的理解或愿意学习它,并且 b) 你的用例证明了这么多工作是合理的。

【讨论】:

以上是关于在保留注释的同时在 java 中修改 YAML的主要内容,如果未能解决你的问题,请参考以下文章

修改现有的 yaml 文件并添加新的数据和注释

保留注释的数据结构格式(YAML 或诸如此类)的往返解析,用于编写配置

在 PyYAML 中保存/转储带有注释的 YAML 文件

在 ruamel.yaml 中保留引号

你如何防止 yaml-cpp 解析器删除所有注释?

边写代码边注释,修改代码同时修改相应的注释