Python dict для текста YAML не сохраняет символы

Вопрос простой, но немного сложно правильно его сформулировать.

По сути, у меня есть словарь со следующими данными:

x = { foo: [1, '\n', 'bar'] }

Когда я конвертирую в yaml, используя pyyaml ​​с yaml.safe_dump(x, default_flow_style=False), я ожидаю, что результат будет таким:

foo:
  - 1
  - '\n'
  - bar

однако я получаю что-то вроде

foo:
  - 1
  - '

    '
  - bar

Символ новой строки фактически интерпретируется вместо того, чтобы передаваться как строка '\n'.

Я просматривал документацию pyyaml, но не видел правильных заклинаний, чтобы эта вещь правильно анализировалась.

Кто-нибудь имел дело с этой же проблемой раньше? Как вы это решили?


Чтобы дать больше контекста, об этом.

У меня есть json, который я хочу преобразовать в yaml.

Файл, в котором есть что-то вроде этого:

{ 
  "content": {
    "Fn::Join": ["\n", [{ "Ref": "parentStackName" }, ""]]
  }
}

конечный результат должен быть таким:

content:
  Fn::Join:
    - "\n"
    - - Ref: parentStackId
      - ''

обратите внимание, что "\n" - это просто строка, а не фактический символ.

Процедура, которую я использую:

  1. Открыть файл
  2. Разобрать json из текста в диктовку
  3. Используйте dict для дампа в yaml

Когда я создаю словарь, вы можете видеть «\n» как часть строки. Когда pyyaml ​​сбрасывает это в yaml, все идет наперекосяк.


person Hector Villarreal    schedule 23.01.2017    source источник
comment
Строка, которую вы показали ('\n'), представляет собой новую строку, а не буквальную обратную косую черту, за которой следует литерал n, поэтому вывод YAML, который вы получаете, имеет смысл. Вы говорите, что хотите преобразовать символы, такие как перевод строки, в их представления управляющей последовательности? Или вы хотите, чтобы ваша строка была '\\n' (буквальная обратная косая черта, буквальная n)?   -  person ThisSuitIsBlackNot    schedule 24.01.2017
comment
строка является литералом '\n', а не новой строкой. Я даю больше контекста в вопросе.   -  person Hector Villarreal    schedule 24.01.2017
comment
"\n" в строке JSON представляет собой новую строку, а не буквальную обратную косую черту, за которой следует литерал n (см. pdf" rel="nofollow noreferrer">spec). Таким образом, вы получаете ожидаемый результат.   -  person ThisSuitIsBlackNot    schedule 24.01.2017
comment
Обратите внимание, что в выводе YAML этот '\n' полностью отличается от "\n". В своем ответе я предположил, что вы хотите вторую версию.   -  person Anthon    schedule 24.01.2017
comment
Почему бы не использовать r'\n' вместо '\n' в x?   -  person boardrider    schedule 25.01.2017


Ответы (2)


Чтобы получить желаемый результат, вы можете использовать возможности ruamel.yaml и обновите стиль потока, который подмножество JSON YAML использует для стиля блокировки:

import sys
import ruamel.yaml
from ruamel.yaml.comments import CommentedMap, CommentedSeq

# because this is a string and not read from file, you need to escape 
# the backslash in \n
json_str = """\
{
  "content": {
    "Fn::Join": ["\\n", [{ "Ref": "parentStackName" }, ""]]
  }
}
"""  


def block_style(base):
    """set all mapping and sequneces to block-style"""
    if isinstance(base, CommentedMap):
        for k in base:
            block_style(base[k])
        base.fa.set_block_style()
    if isinstance(base, list):
        for item in base:
            block_style(item)
        base.fa.set_block_style()
    return base


data = ruamel.yaml.round_trip_load(json_str)
block_style(data)
ruamel.yaml.round_trip_dump(data, sys.stdout)

дает:

content:
  Fn::Join:
  - "\n"
  - - Ref: parentStackName
    - ''

ruamel.yaml — это обновленная версия PyYAML (отказ от ответственности: я автор). Он поддерживает спецификацию YAML 1.2 (с 2009 г.), которая делает YAML более похожим на полный надмножество JSON и позволяет вам читать JSON с помощью синтаксического анализатора ruamel.yaml (PyYAML поддерживает только большую часть спецификации YAML 1.1).

В режиме «туда и обратно» усовершенствования ruamel.yaml включают в себя поддержание потока, соответственно. блочный стиль составных узлов (сопоставлений и последовательностей) на индивидуальной основе (а также различные стили цитирования, комментарии и имена тегов). Что делает block_style(), так это рекурсивно изменяет «атрибут потока» .fa на блочный стиль для всех составных узлов.

person Anthon    schedule 24.01.2017
comment
отлично, это делает именно то, что мне нужно. Он также сохраняет порядок атрибутов :) Отличная работа! - person Hector Villarreal; 24.01.2017

Используйте необработанные строки, если строка содержит специальные символы:

$ cat /tmp/tmp.py

import yaml

foo = 'foo'
x = { foo: [1, r'\n', 'bar'] }
y = yaml.safe_dump(x, default_flow_style=False)
print(y)

$ python3 /tmp/tmp.py
foo:
- 1
- \n
- bar
person boardrider    schedule 24.01.2017