C#在yaml中解析和更改字符串

时间:2015-05-27 20:48:09

标签: c# python parsing yaml yamldotnet

我正在寻找一种方法来解析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文件,更改字符串并保存数据而不影响文档结构?也许你还有其他想法如何解决这个问题。基本上我想迭代文档顶部的每个字符串,并对字符串进行一些修改。 任何提示都赞赏。

2 个答案:

答案 0 :(得分:2)

YAML规范有this to say

  

在表示模型中,映射键没有顺序。要序列化映射,必须对其键进行排序。此顺序是序列化详细信息,在编写表示图时不应使用(因此保留应用程序数据)。在节点顺序很重要的每种情况下,必须使用序列。例如,有序映射可以表示为映射序列,其中每个映射是单个键:值对。 YAML为这种情况提供了方便的紧凑符号。

所以你真的不应该期望YAML在加载和保存文档时保持任何顺序。

话虽这么说,我完全明白你的来源。由于YAML文件适用于人类,因此保持一定的顺序绝对有帮助。不幸的是,由于规范,大多数实现将使用无序数据结构来表示键/值映射。在C#和Python中,这将是一个字典;和字典是没有订单的设计。

但是C#和Python都有排序的字典类型OrderedDictionaryOrderedDict,至少对于Python来说,过去已经做了一些努力来维护使用有序字典的密钥顺序: / p>

这就是Python方面;我确信C#实现也有类似的努力。

答案 1 :(得分:1)

大多数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.yamlPyYAML以及其他语言和在线YAML转换服务中的许多YAML解析器的默认行为。对于某些解析器,这是唯一提供的行为。

我启动ruamel.yaml作为PyYAML的增强的原因是从手工制作的YAML到内部数据,再到YAML,导致更好的人类可读性(我称之为 round-tripping < / em>),并保留更多信息(尤其是评论)。

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.yaml键序保持的内置增强功能)你可以提供特殊的发射器,或者在较低级别挂钩系统,覆盖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#

中进一步处理是可以接受的