带有PyYAML的单引号中的单个字符串

时间:2016-05-07 22:02:34

标签: python yaml pyyaml

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

one: valueOne
two: valueTwo
three: valueThree

我希望其中一个字符串用单引号括起来:

one: valueOne
two: valueTwo
three: 'valueThree'

更改default_style中的yaml_dump参数会影响整个文件,这是不希望的。我想在单词的开头和结尾添加单引号,我希望用它包围:

valueThreeVariable = "'" + valueThreeVariable + "'"

然而,这最终会导致倾销的YAML看起来像这样:

one: valueOne
two: valueTwo
three: '''valueThree'''

我尝试使用unicode或raw字符串以各种方式转义单引号,但都无济于事。 如何只将我的一个YAML值作为用单引号括起来的字符串?

2 个答案:

答案 0 :(得分:4)

您可以将此类功能移植到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

不会抛出断言错误。

要从转储器开始,您可以“转换”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'])

但是这不能被转储,因为转储器不知道SingleQuotedScalarString。您可以通过不同的方式解决这个问题,以下内容扩展了ruamel.yaml的{​​{1}}类:

RoundTripRepresenter

再一次不会抛出错误。上面的内容原则上可以在PyYAML和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 / safe_load中完成,但是您需要编写代码来保留键排序以及一些基本功能。 (除此之外,PyYAML仅支持较旧的YAML 1.1标准,而不支持2009年的YAML 1.2标准。)

要在不使用明确safe_dump转换的情况下使加载工作正常,您可以在调用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 类,但实际的更改方法很小,很容易修补。

结合以上所有内容并清理一下:

RoundTripConstructor

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

使用更现代的版本(ruamel.yaml&gt; 0.14),您可以:

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

并保留单引号。

答案 1 :(得分:0)

我让你的 yaml 事先没有引号,无论之前有没有引号,结果都是一样的。

方法如下:

import yaml

myyaml = """
one: valueOne
two: valueTwo
three: valueThree
"""

# loading the yaml

yaml_dict = yaml.load(myyaml, yaml.FullLoader)
print(yaml_dict)

{'one': 'valueOne', 'two': 'valueTwo', 'three': 'valuethree'}

# Define the following class:

class SingleQuoted(str):
    pass

def single_quoted_presenter(dumper, data):
    return dumper.represent_scalar('tag:yaml.org,2002:str', data, style="'")

yaml.add_representer(SingleQuoted, single_quoted_presenter)

yaml_dict.update(three = SingleQuoted('valuethree'))

# Now dump as yaml

yaml.dump(yaml_dict, sys.stdout, sort_keys=False, default_flow_style=False)

# save it in a file by using an open file instead of stdout

,它给出了所需的 yaml,如下所示:

one: valueOne
two: valueTwo
three: 'valueThree'