用PyYAML用单引号引起来的单个字符串

3ya*_*uya 3 python yaml pyyaml

当我使用PyYAML在Python中编辑YAML文件时,我所有的字符串值都保存回没有引号的原始文件中。

one: valueOne
two: valueTwo
three: valueThree
Run Code Online (Sandbox Code Playgroud)

我希望这些字符串之一用单引号引起来:

one: valueOne
two: valueTwo
three: 'valueThree'
Run Code Online (Sandbox Code Playgroud)

更改中的default_style参数yaml_dump会影响整个文件,这是不希望的。我考虑过在要包围的字符串的开头和结尾添加单引号:

valueThreeVariable = "'" + valueThreeVariable + "'"
Run Code Online (Sandbox Code Playgroud)

但是,最终会得到如下所示的转储YAML:

one: valueOne
two: valueTwo
three: '''valueThree'''
Run Code Online (Sandbox Code Playgroud)

我尝试使用unicode或原始字符串以各种方式转义单引号,但都无济于事。如何仅使我的YAML值之一成为用单引号引起来的字符串?

Ant*_*hon 6

您可以将此类功能嫁接到PyYAML上,但这并不容易。映射中的值three必须是与普通字符串不同的类的某个实例,否则YAML转储器不知道它必须做一些特殊的事情,并且该实例将作为带引号的字符串转储。在加载带有单引号的标量时,需要将其创建为此类的实例。除此之外,您可能不希望您的dict/ 密钥mapping像PyYAML默认那样被打乱。

对于块样式标量,我在PyYAML派生的ruamel.yaml中执行与上面类似的操作:

import ruamel.yaml

yaml_str = """\
one: valueOne
two: valueTwo
three: |-
  valueThree
"""

data = ruamel.yaml.round_trip_load(yaml_str)
assert ruamel.yaml.round_trip_dump(data) == yaml_str
Run Code Online (Sandbox Code Playgroud)

不会引发断言错误。


要开始使用转储程序,您可以“转换” valueThree字符串:

import ruamel.yaml
from ruamel.yaml.scalarstring import ScalarString

yaml_str = """\
one: valueOne
two: valueTwo
three: 'valueThree'
"""

class SingleQuotedScalarString(ScalarString):
    def __new__(cls, value):
        return ScalarString.__new__(cls, value)

data = ruamel.yaml.round_trip_load(yaml_str)
data['three'] = SingleQuotedScalarString(data['three'])
Run Code Online (Sandbox Code Playgroud)

但这不能转储,因为转储者不了解SingleQuotedScalarString。您可以解决以不同的方式,下面扩展ruamel.yamlRoundTripRepresenter类:

from ruamel.yaml.representer import RoundTripRepresenter
import sys

def _represent_single_quoted_scalarstring(self, data):
    tag = None
    style = "'"
    if sys.version_info < (3,) and not isinstance(data, unicode):
        data = unicode(data, 'ascii')
    tag = u'tag:yaml.org,2002:str'
    return self.represent_scalar(tag, data, style=style)

RoundTripRepresenter.add_representer(
    SingleQuotedScalarString,
    _represent_single_quoted_scalarstring)

assert ruamel.yaml.round_trip_dump(data) == yaml_str
Run Code Online (Sandbox Code Playgroud)

再一次不会引发错误。以上可以在PyYAML和safe_load/ safe_dump原则上完成,但是您将需要编写代码来保留键顺序以及一些基本功能。(除此之外,PyYAML仅支持较早的YAML 1.1标准,而不支持2009年以来的YAML 1.2标准)。

为了使加载工作不使用显式data['three'] = SingleQuotedScalarString(data['three'])转换,可以在调用之前添加以下内容ruamel.yaml.round_trip_load()

from ruamel.yaml.constructor import RoundTripConstructor
from ruamel.yaml.nodes import ScalarNode
from ruamel.yaml.compat import text_type

def _construct_scalar(self, node):
    if not isinstance(node, ScalarNode):
        raise ConstructorError(
            None, None,
            "expected a scalar node, but found %s" % node.id,
            node.start_mark)

    if node.style == '|' and isinstance(node.value, text_type):
        return PreservedScalarString(node.value)
    elif node.style == "'" and isinstance(node.value, text_type):
        return SingleQuotedScalarString(node.value)
    return node.value

RoundTripConstructor.construct_scalar = _construct_scalar
Run Code Online (Sandbox Code Playgroud)

有多种方法可以执行上述操作,包括对RoundTripConstructor类进行子类化,但是更改的实际方法很小,可以轻松进行修补。


结合以上所有内容并进行一些清理,您将得到:

import ruamel.yaml
from ruamel.yaml.scalarstring import ScalarString
from ruamel.yaml.representer import RoundTripRepresenter
from ruamel.yaml.constructor import RoundTripConstructor
from ruamel.yaml.nodes import ScalarNode
from ruamel.yaml.compat import text_type, PY2


class SingleQuotedScalarString(ScalarString):
    def __new__(cls, value):
        return ScalarString.__new__(cls, value)


def _construct_scalar(self, node):
    if not isinstance(node, ScalarNode):
        raise ConstructorError(
            None, None,
            "expected a scalar node, but found %s" % node.id,
            node.start_mark)

    if node.style == '|' and isinstance(node.value, text_type):
        return PreservedScalarString(node.value)
    elif node.style == "'" and isinstance(node.value, text_type):
        return SingleQuotedScalarString(node.value)
    return node.value

RoundTripConstructor.construct_scalar = _construct_scalar


def _represent_single_quoted_scalarstring(self, data):
    tag = None
    style = "'"
    if PY2 and not isinstance(data, unicode):
        data = unicode(data, 'ascii')
    tag = u'tag:yaml.org,2002:str'
    return self.represent_scalar(tag, data, style=style)

RoundTripRepresenter.add_representer(
    SingleQuotedScalarString,
    _represent_single_quoted_scalarstring)


yaml_str = """\
one: valueOne
two: valueTwo
three: 'valueThree'
"""

data = ruamel.yaml.round_trip_load(yaml_str)
assert ruamel.yaml.round_trip_dump(data) == yaml_str
Run Code Online (Sandbox Code Playgroud)

仍然运行而没有断言错误,即转储输出等于输入。如前所述,您可以在PyYAML中执行此操作,但是它需要更多的编码。


使用更现代的版本(ruamel.yaml> 0.14),您可以执行以下操作:

yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True

data = yaml.load(yaml_str)
yaml.dump(data, sys.stdout)
Run Code Online (Sandbox Code Playgroud)

并保留单引号。