YAML - выгрузка вложенного объекта без типов / тегов - PullRequest
1 голос
/ 24 апреля 2019

Я пытаюсь сбросить некоторые объекты Python в YAML.

В настоящее время, независимо от библиотеки YAML (pyyaml, oyaml или ruamel), у меня возникла проблема, когда вызов .dump(MyObject) дает мне правильный YAML, но, похоже, добавляет много метаданных о Python объекты, которые я не хочу, в форме, которая выглядит следующим образом:

!!python/object:MyObject и другие похожие строки.

Мне не нужно перестраивать объекты из YAML, поэтому я вполне могу удалить эти метаданные полностью

Другие вопросы по SO указывают на то, что общим решением этого является использование safe_dump вместо dump.

Однако safe_dump, похоже, не работает для вложенных объектов (или объектов вообще), так как выдает эту ошибку:

yaml.representer.RepresenterError: ('cannot represent an object', MyObject)

Я вижу, что обычным обходным решением здесь является ручное указание Представителей для объектов, которые я пытаюсь создать. Моя проблема здесь в том, что мои объекты являются сгенерированным кодом, который я не могу контролировать. Я также буду сбрасывать различные предметы.

Итог: есть ли способ выгрузить вложенные объекты, используя .dump, но где метаданные не добавляются?

1 Ответ

3 голосов
/ 24 апреля 2019

Хотя слова «правильный YAML» не совсем точны, и их лучше сформулировать как «Вывод YAML выглядит так, как вы хотите, за исключением информации тега», к счастью, это дает информация о том, как вы хотите, чтобы ваш YAML выглядел, поскольку существует бесконечное количество способов создания дампов объектов.

Если вы сбрасываете объект, используя ruamel.yaml:

import sys
import ruamel.yaml

class MyObject:
   def __init__(self, a, b):
      self.a = a
      self.b = b
      self.c = [a, b]

data = dict(x=MyObject(42, -1))


yaml = ruamel.yaml.YAML(typ='unsafe')
yaml.dump(data, sys.stdout)

это дает:

x: !!python/object:__main__.MyObject
  a: 42
  b: -1
  c: [42, -1]

У вас есть тег !!python/object:__main__.MyObject (ваш может отличаться в зависимости от того, где класс определен и т. д.), и каждый из атрибутов класса выводится как ключи отображения.

Существует несколько способов избавиться от тега в этом дампе:

Регистрация классов

Добавьте classmethod с именем to_yaml() к каждому из ваших классов и зарегистрировать эти классы. Вы должны сделать это для каждого из ваших классов, но это позволяет вам использовать безопасный самосвал. Пример того, как сделать это можно найти в Документация

Post-процесс

Довольно просто постобработать вывод и удалить теги, которые для объектов всегда встречаются в строке до отображения, и вы можете удалить от !!python до конца строки

def strip_python_tags(s):
    result = []
    for line in s.splitlines():
        idx = line.find("!!python/")
        if idx > -1:
            line = line[:idx]
        result.append(line)
    return '\n'.join(result)

yaml.encoding = None
yaml.dump(data, sys.stdout, transform=strip_python_tags)

и это дает:

x: 
  a: 42
  b: -1
  c: [42, -1]

Так как ахоры сбрасываются перед тегом, это "удаление с !!python до конца строки ", также работает при сбросе объекта, несколько ссылок.

Поменять самосвал

Вы также можете изменить подпрограмму небезопасного дампера для сопоставлений на распознать тег, используемый для объектов, и изменить тег на «обычный» один для dict / mapping (для которого обычно тег не выводится)

yaml.representer.org_represent_mapping = yaml.representer.represent_mapping

def my_represent_mapping(tag, mapping, flow_style=None):
    if tag.startswith("tag:yaml.org,2002:python/object"):
        tag = u'tag:yaml.org,2002:map'
    return yaml.representer.org_represent_mapping(tag, mapping, flow_style=flow_style)

yaml.representer.represent_mapping = my_represent_mapping

yaml.dump(data, sys.stdout)

и это дает еще раз:

x:
  a: 42
  b: -1
  c: [42, -1]

Эти два последних метода работают для всех экземпляров всех классов Python, которые вы определяете, без дополнительной работы.

...