c#解析和更改yaml中的字符串
本文关键字:yaml 字符串 | 更新日期: 2023-09-27 17:50:53
我正在寻找一种方法来解析yaml文件并更改每个字符串,然后保存文件而不改变原始文件的结构。在我看来,我不应该使用正则表达式,而应该使用某种yaml解析器。示例yaml输入如下:
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06
customer:
given: Dorothy
items:
- part_no: A4786
descrip: Water Bucket (Filled)
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
...
所需输出:receipt: ###Oz-Ware Purchase Invoice###
date: ###2007-08-06###
customer:
given: ###Dorothy###
items:
- part_no: ###A4786###
descrip: ###Water Bucket (Filled)###
- part_no: ###E1628###
descrip: ###High Heeled "Ruby" Slippers###
size: ###8###
bill-to: ###&id001###
street: |
###123 Tornado Alley
Suite 16###
city: ###East Centerville###
state: ###KS###
ship-to: ###*id001###
specialDelivery: >
###Follow the Yellow Brick
Road to the Emerald City.###
...
是否有一个好的yaml解析器,可以处理复杂的yaml文件,改变字符串和保存数据回来,而不影响文档的结构?也许你有别的办法解决这个问题。基本上,我想从文档的顶部遍历每个字符串,并对字符串进行一些修改。
YAML规范是这样说的:
在表示模型中,映射键没有顺序。要序列化映射,必须对其键施加排序。这个顺序是一个序列化细节,在组成表示图时不应该使用它(因此也不应该用于保存应用程序数据)。在节点顺序重要的任何情况下,都必须使用序列。例如,一个有序映射可以表示为一个映射序列,其中每个映射是一个键:值对。YAML为这种情况提供了方便的紧凑符号。
所以你真的不应该期望YAML在加载和保存文档时保持任何顺序。
话虽如此,我完全理解你的出发点。由于YAML文档是为人类准备的,因此保持一定的顺序绝对是有帮助的。不幸的是,由于规范的原因,大多数实现将使用无序的数据结构来表示键/值映射。在c#和Python中,这将是一个字典;字典的设计是没有顺序的。但是c#和Python都有有序字典类型,OrderedDictionary
和OrderedDict
,至少对于Python来说,过去已经有一些努力来使用有序字典来维护键顺序:
-
!!omap
类型为特殊有序映射。PyYAML支持。 - PyYAML票证29讨论了可能包括
OrderedLoader
。还有一个简短的解决方案,在两者之间使用YAML构造函数,并在最后实现加载器。 - PyYAML ticket 161有一个recipe也提供了这个功能。
- 最后,还有另一个堆栈溢出问题,涉及将YAML加载到
OrderedDict
s中。
这是Python的一面;我相信c#的实现也有类似的努力。
大多数YAML解析器都是为读取由其他程序编写或由人类编辑的YAML以及编写供其他程序读取的YAML而构建的。众所周知,缺乏的是解析器编写人类仍然可读的YAML的能力:
- 映射键的顺序未定义
- 注释被丢弃
- 标量文字块样式(如果有的话)被删除
- 标量周围的间距被丢弃
- 如果有标量折叠信息,则删除
加载手工制作的YAML文件的转储将产生与初始加载相同的内部数据结构,但是中间转储通常看起来不像原始(手工制作的)YAML。
如果你有一个Python程序:
import ruamel.yaml as yaml
yaml_str = """'
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06
customer:
given: Dorothy
items:
- part_no: A4786
descrip: Water Bucket (Filled)
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
"""
data1 = yaml.load(yaml_str, Loader=yaml.Loader)
dump_str = yaml.dump(data1, Dumper=yaml.Dumper)
data2 = yaml.load(dump_str, Loader=yaml.Loader)
则下列断言成立:
assert data1 == data2
assert dump_str != yaml_str
中间dump_str
看起来像:
bill-to: &id001 {city: East Centerville, state: KS, street: '123 Tornado Alley
Suite 16
'}
customer: {given: Dorothy}
date: 2007-08-06
items:
- {descrip: Water Bucket (Filled), part_no: A4786}
- {descrip: High Heeled "Ruby" Slippers, part_no: E1628, size: 8}
receipt: Oz-Ware Purchase Invoice
ship-to: *id001
specialDelivery: 'Follow the Yellow Brick Road to the Emerald City.
'
以上是ruamel的默认行为。yaml, PyYAML和许多其他语言的yaml解析器和在线yaml转换服务。对于某些解析器,这是提供的唯一行为。
我开始做巧克力的原因。yaml作为PyYAML的一个增强是使从手工制作的yaml到内部数据,再到yaml,产生更好的人类可读的东西(我称之为往返),并保留更多的信息(特别是注释)。
data = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)
print yaml.dump(data, Dumper=yaml.RoundTripDumper)
给你:
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06
customer:
given: Dorothy
items:
- part_no: A4786
descrip: Water Bucket (Filled)
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: 'Follow the Yellow Brick Road to the Emerald City.
'
我关注的是注释、键、顺序和文字块样式。标量和折叠标量之间的间距(还)不特殊。
从那里开始(您也可以在PyYAML中这样做,但您将没有ruamel的内置增强)。您可以提供特殊的发射器,或者在较低的级别上钩入系统,覆盖emitter.py
中的一些方法(并确保您可以调用不需要处理的情况的原件:
def rewrite_write_plain(self, text, split=True):
if self.state == self.expect_block_mapping_simple_value:
text = '###' + text + '###'
while self.column < 20:
text = ' ' + text
self.column += 1
self._org_write_plain(text, split)
def rewrite_write_literal(self, text):
if self.state == self.expect_block_mapping_simple_value:
last_nl = False
if text and text[-1] == ''n':
last_nl = True
text = text[:-1]
text = '###' + text + '###'
if False:
extra_indent = ''
while self.column < 15:
text = ' ' + text
extra_indent += ' '
self.column += 1
text = text.replace(''n', ''n' + extra_indent)
if last_nl:
text += ''n'
self._org_write_literal(text)
def rewrite_write_single_quoted(self, text, split=True):
if self.state == self.expect_block_mapping_simple_value:
last_nl = False
if text and text[-1] == u''n':
last_nl = True
text = text[:-1]
text = u'###' + text + u'###'
if last_nl:
text += u''n'
self.write_folded(text)
def rewrite_write_indicator(self, indicator, need_whitespace,
whitespace=False, indention=False):
if indicator and indicator[0] in u"*&":
indicator = u'###' + indicator + u'###'
while self.column < 20:
indicator = ' ' + indicator
self.column += 1
self._org_write_indicator(indicator, need_whitespace, whitespace,
indention)
dumper._org_write_plain = dumper.write_plain
dumper.write_plain = rewrite_write_plain
dumper._org_write_literal = dumper.write_literal
dumper.write_literal = rewrite_write_literal
dumper._org_write_single_quoted = dumper.write_single_quoted
dumper.write_single_quoted = rewrite_write_single_quoted
dumper._org_write_indicator = dumper.write_indicator
dumper.write_indicator = rewrite_write_indicator
print yaml.dump(data, Dumper=dumper, indent=4)
给你:
receipt: ###Oz-Ware Purchase Invoice###
date: ###2007-08-06###
customer:
given: ###Dorothy###
items:
- part_no: ###A4786###
descrip: ###Water Bucket (Filled)###
- part_no: ###E1628###
descrip: ###High Heeled "Ruby" Slippers###
size: ###8###
bill-to: ###&id001###
street: |
###123 Tornado Alley
Suite 16###
city: ###East Centerville###
state: ###KS###
ship-to: ###*id001###
specialDelivery: >
###Follow the Yellow Brick Road to the Emerald City.###
在c#中是可以接受的