pythonpyyaml

Dump JSON as multline String in Python with PyYaml


I need to do a roundtrip with some Kubernetes Resources to alter it in some way with Python (3.11). Now i have t following ConfigMap-Resource:

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-map
data:
  sample.json: |-
    [
      {
        "name": "foo",
        "description": "bar"
      }
    ]

After loading it, altering it i want to dump it back, while preserving the former form. Unfortunately, i only get the following output:

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-map
data:
  sample.json: "[\n {\n "name": "foo",\n"description": "bar"\n}\n]"

I tried various things, but without any luck e.g with different representer combination as seen in https://github.com/yaml/pyyaml/issues/240 Does anyone have an idea how to do it? Thanks a lot!


Solution

  • Use yaml.add_representer to indicate the function you want to use to render strings. (The particular representer used here is taken from Issue 240 as well.)

    import yaml
    
    def str_presenter(dumper, data):
        """configures yaml for dumping multiline strings
        Ref: https://stackoverflow.com/questions/8640959/how-can-i-control-what-scalar-form-pyyaml-uses-for-my-data"""
        if data.count('\n') > 0:  # check for multiline string
            return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
        return dumper.represent_scalar('tag:yaml.org,2002:str', data)
    
    
    data = """apiVersion: v1
    kind: ConfigMap
    metadata:
      name: sample-map
    data:
      sample.json: |-
        [
          {
            "name": "foo",
            "description": "bar"
          }
        ]"""
    
    d = yaml.load(data, yaml.Loader)
    
    yaml.add_representer(str, str_presenter)
    
    print(yaml.dump(d))
    

    (Note, though, that this does not preserver the original order of the keys. I don't recall if that's possible, but I think it's outside the scope of this question.)

    Output:

    $ python3 test.py
    apiVersion: v1
    data:
      sample.json: |-
        [
          {
            "name": "foo",
            "description": "bar"
          }
        ]
    kind: ConfigMap
    metadata:
      name: sample-map