ruamel.yaml:将注释固定到下一个数据项而不是前一个数据项

如何解决ruamel.yaml:将注释固定到下一个数据项而不是前一个数据项

我在使用 ruamel.yaml 和往返加载器时观察到了一个有点令人困惑的行为。 这可能是因为 ruamel.yaml 自动确定评论应该连接到哪个数据项并非易事。

在下面的例子中,我想一直保留评论。如果我告诉 ruamel.yaml 它应该考虑与下一个数据项相关的所有评论(即评论在“其他”之前),这应该是可能的。

这能做到吗?

如果是:如何?

data_was_a_dict = """\
---
data: was_a_dict
main_dict:
  data: 
    some: data

# this comment gets always lost
other: data
"""

data_was_a_str = """\
---
data: was_a_str
main_dict:
  data: a_string  

# this gets shifted or stays
other: data
"""

import ruamel.yaml,sys


yaml = ruamel.yaml.YAML()
for text in [data_was_a_dict,data_was_a_str]:
    for new_data in ["new_text",{"something": "else"}]:
        data = yaml.load(text)

        data["main_dict"]["data"] = new_data
        yaml.dump(data,sys.stdout)
        print("==========================")

输出

data: was_a_dict
main_dict:
  data: new_text
other: data
==========================
data: was_a_dict
main_dict:
  data:
    something: else
other: data
==========================
data: was_a_str
main_dict:
  data: new_text

# this gets shifted or stays
other: data
==========================
data: was_a_str
main_dict:
  data:

# this gets shifted or stays
    something: else
other: data
==========================

==========================================

感谢 Anthon 的更新:

def replace_dict(target,key,new_dict):
    def get_last_key(dct):
        keys = [key for key in dct.keys()]
        return keys[-1]
        
    old_dict = target[key]
    if old_dict and new_dict:
        # if new_dict is empty,we will lose the comment. 
        # That's fine for Now since this should not happen in my case and I don't kNow yet where to attach 
        # the comment in that case
        last_comment = old_dict.ca.items.get(get_last_key(old_dict),None)
        if last_comment:
            actual_comment = last_comment[2]
            actual_comment.value = clean_comment(actual_comment.value)
            if actual_comment.value:
                if not isinstance(new_dict,ruamel.yaml.comments.CommentedMap):
                    new_dict = ruamel.yaml.comments.CommentedMap(new_dict)                
                new_dict.ca.items[get_last_key(new_dict)] = last_comment
    target[key] = new_dict
    
def clean_comment(txt: str) -> str:
    _,_,after = txt.partition("\n")
    if after:
        return "\n" + after
    return ""

data_was_a_dict = """\
---
main_dict:
  place: holder
  sub_dict: # this stays
    item1: value1
    item2: value2 # this is removed
    
# this stays    
other: data
"""

import ruamel.yaml,sys
import json

yaml = ruamel.yaml.YAML()

data = yaml.load(data_was_a_dict)
replace_dict(data["main_dict"],"sub_dict",{"item_new": "value_new"})

yaml.dump(data,sys.stdout)

给予

main_dict:
  place: holder
  sub_dict: # this stays
    item_new: value_new
# this stays    
other: data

解决方法

我不确定以下关于保留评论的 documented 行为有何令人困惑:

除非您严重改变组件的结构,否则这种保存通常不会被破坏 (删除字典中的一个键,删除列表条目)。重新分配值或替换列表项等都可以。

在您转储的四种组合中的三种中,您首先替换了一个 简单值乘以复合值,否则删除包含的复合值 一共评论信息。

在当前所有版本(即 "\n\n# this gets shifted or stays" 的值的扩展行尾注释,因为它以换行符开头,这意味着没有实际的 在与之关联的键的行尾添加注释..

在您的 data_was_a_dict 中,与键 some 关联的注释以及您是否替换 CommentedMapdict 子类型,带有对 它的 .ca 属性)与字符串或字典没有区别,因为带有注释的数据结构被完全替换。

在您的 data_was_a_str YAML 文档中,它与键 data 相关联 在“CommentedMap 的级别高于其他文档”。如果你更换 它的值与另一个字符串的输出将类似于输入。如果你 添加一个全新的子结构,注释被解释为介于 键及其(复合)值。

要获得您所期望的结果,您必须检查是否有与 键 data 并将其移动到与键 something 相关联,这可以 不是普通 dict 上的键(它必须是 CommentedMap)。 在删除/覆盖附加注释的数据结构的组合中,您必须检查注释并在删除之前移动它。在用复合值替换简单值的组合中,您可以在赋值之后移动注释(给定合适的复合值,如 CommentedMap)。所以是的,您想要的是可能的,但并非微不足道,这些将依赖于未记录的功能,这些功能将在即将推出的版本中发生变化。

import sys
import ruamel.yaml

data_was_a_dict = """\
data: was_a_dict
main_dict:
  data: 
    some: data

# this comment gets always lost
other: data
"""

data_was_a_str = """\
data: was_a_str
main_dict:
  data: a_string  

# this gets shifted or stays
other: data
"""

yaml = ruamel.yaml.YAML()

data = yaml.load(data_was_a_dict)
print(data['main_dict']['data'].ca)
data = yaml.load(data_was_a_str)
print(data['main_dict'].ca)

给出:

Comment(comment=None,items={'some': [None,None,CommentToken('\n\n# this comment gets always lost\n',line: 5,col: 0),None]})
Comment(comment=None,items={'data': [None,CommentToken('\n\n# this gets shifted or stays\n',line: 4,None]})

我正在考虑替换 ruamel.yaml 的评论扫描和附件,这将允许 用户可选择拆分(在第一个空行上),允许用户 指定将评论信息附加到前面和/或后面的“数据”。 分配可能会受到缩进级别的影响。文档 甚至可能会更新以反映往返将支持更少 重组数据时保留评论的严格限制。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?