Parsing YAML, get line numbers even in ordered maps

后端 未结 1 715
予麋鹿
予麋鹿 2021-01-24 14:07

I need to get the line numbers of certain keys of a YAML file.

Please note, this answer does not solve the issue: I do use ruamel.yaml, and the answers do not work with

相关标签:
1条回答
  • 2021-01-24 14:33

    This issue is not that you are using !omap and that it doesn't give you the line-numbers as with "normal" mappings. That should be clear from the fact that you get 4 from doing print(data['key1']['key4'].lc.line) (where key4 is a key in the outer !omap).

    As this answers indicates,

    you can access the property lc on collection items

    The value for data['key1']['key4'] is a collection item (another !omap), but the value for data['key1']['key2'] is not a collection item but a, built-in, python string, which has no slot to store the lc attribute.

    To get an .lc attribute on a non-collection like a string you have to subclass the RoundTripConstructor, to use something like the classes in scalarstring.py (with __slots__ adjusted to accept the lc attribute and then transfer the line information available in the nodes to that attribute and then set the line, column information:

    import sys
    import ruamel.yaml
    
    yaml_str = """
    key1: !!omap
      - key2: item2
      - key3: item3
      - key4: !!omap
        - key5: 'item5'
        - key6: |
            item6
    """
    
    class Str(ruamel.yaml.scalarstring.ScalarString):
        __slots__ = ('lc')
    
        style = ""
    
        def __new__(cls, value):
            return ruamel.yaml.scalarstring.ScalarString.__new__(cls, value)
    
    class MyPreservedScalarString(ruamel.yaml.scalarstring.PreservedScalarString):
        __slots__ = ('lc')
    
    class MyDoubleQuotedScalarString(ruamel.yaml.scalarstring.DoubleQuotedScalarString):
        __slots__ = ('lc')
    
    class MySingleQuotedScalarString(ruamel.yaml.scalarstring.SingleQuotedScalarString):
        __slots__ = ('lc')
    
    class MyConstructor(ruamel.yaml.constructor.RoundTripConstructor):
        def construct_scalar(self, node):
            # type: (Any) -> Any
            if not isinstance(node, ruamel.yaml.nodes.ScalarNode):
                raise ruamel.yaml.constructor.ConstructorError(
                    None, None,
                    "expected a scalar node, but found %s" % node.id,
                    node.start_mark)
    
            if node.style == '|' and isinstance(node.value, ruamel.yaml.compat.text_type):
                ret_val = MyPreservedScalarString(node.value)
            elif bool(self._preserve_quotes) and isinstance(node.value, ruamel.yaml.compat.text_type):
                if node.style == "'":
                    ret_val = MySingleQuotedScalarString(node.value)
                elif node.style == '"':
                    ret_val = MyDoubleQuotedScalarString(node.value)
                else:
                    ret_val = Str(node.value)
            else:
                ret_val = Str(node.value)
            ret_val.lc = ruamel.yaml.comments.LineCol()
            ret_val.lc.line = node.start_mark.line
            ret_val.lc.col = node.start_mark.column
            return ret_val
    
    
    yaml = ruamel.yaml.YAML()
    yaml.Constructor = MyConstructor
    
    data = yaml.load(yaml_str)
    print(data['key1']['key4'].lc.line)
    print(data['key1']['key2'].lc.line)
    print(data['key1']['key4']['key6'].lc.line)
    

    Please note that the output of the last call to print is 6, as the literal scalar string starts with the |.

    If you also want to dump data, you'll need to make a Representer aware of those My.... types.

    0 讨论(0)
提交回复
热议问题