2016-05-07 1 views
1

Lorsque je modifie un fichier YAML en Python avec PyYAML, toutes mes valeurs de chaîne sont sauvegardées dans le fichier d'origine sans guillemets.Une seule chaîne de guillemets simples avec PyYAML

one: valueOne 
two: valueTwo 
three: valueThree 

Je voulais une de ces cordes pour entourer avec des guillemets simples:

one: valueOne 
two: valueTwo 
three: 'valueThree' 

Modification du paramètre default_style dans yaml_dump affecte le fichier entier, qui n'est pas souhaitée. Je pensais à ajouter des guillemets simples au début et à la fin d'une chaîne que je veux être entouré de:

valueThreeVariable = "'" + valueThreeVariable + "'" 

Cependant, cela finit par un YAML sous-évaluées qui ressemble à ceci:

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

I J'ai essayé d'échapper à la citation unique de différentes façons, en utilisant des chaînes unicode ou brutes, mais en vain. Comment puis-je faire qu'une seule de mes valeurs YAML soit une chaîne entourée de guillemets simples?

+0

Pourquoi voulez-vous faire cela? –

+0

Voici quelque chose à essayer. Je ne sais pas si ça va marcher - je n'ai jamais utilisé PyYaml. Essayez ceci: valueThreeVariable = "'{0}'" .format (valueThreeVariable). Notez les espaces à l'intérieur des guillemets autour des guillemets simples. –

+0

@TomBarron malheureusement, cela formate la chaîne unique dans ''' 'StringHere' ''' façon – 3yakuya

Répondre

1

Vous pouvez greffer une telle fonctionnalité sur PyYAML mais ce n'est pas facile. La valeur dans le mappage pour three doit être une instance d'une classe différente d'une chaîne normale, sinon le dumper YAML ne sait pas qu'il doit faire quelque chose de spécial et cette instance est sauvegardée sous forme de chaîne avec des guillemets. Lors du chargement de scalaires avec des guillemets simples doivent être créés en tant qu'instances de cette classe. Et à part ça, vous ne voulez probablement pas que les clés de votre dict/mapping soient brouillées comme le fait PyYAML par défaut.

je fais quelque chose de similaire à ce qui précède dans mon dérivé PyYAML ruamel.yaml pour le style bloc scalaires:

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 

ne jette pas une erreur d'assertion.


Pour commencer par le dumper, vous pouvez « convertir » la chaîne 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']) 

mais cela ne peut pas être sous-évaluées, comme le dumper ne connaît pas le SingleQuotedScalarString. Vous pouvez résoudre ce de différentes manières, ce qui suit étend la classe de RoundTripRepresenterruamel.yaml:

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 

Encore une fois ne jette pas une erreur. Ce qui précède peut être fait dans PyYAML et le safe_load/safe_dump en principe, mais vous auriez besoin d'écrire du code pour préserver l'ordre des clés, ainsi que certaines fonctionnalités de base. (En dehors de cela, PYYAML ne supporte que l'ancienne norme YAML 1.1 et non la norme YAML 1.2 de 2009).

Pour obtenir le chargement de travailler sans utiliser la conversion data['three'] = SingleQuotedScalarString(data['three']) explicite, vous pouvez ajouter ce qui suit avant l'appel à 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 

Il existe différentes façons de le faire ci-dessus, y compris le sous-classement de la classe RoundTripConstructor, mais la méthode de changement est petite et peut facilement être corrigée.


La combinaison de ce qui précède et nettoyer un peu, vous obtenez:

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 

qui fonctionne toujours sans erreur d'assertion, à savoir avec une sortie de décharge d'entrée égal. Comme indiqué, vous pouvez le faire dans PyYAML, mais cela nécessite beaucoup plus de codage.


Avec une version plus moderne (ruamel.yaml> 0.14) vous pouvez faire:

yaml = ruamel.yaml.YAML() 
yaml.preserve_quotes = True 

data = yaml.load(yaml_str) 
yaml.dump(data, sys.stdout) 

et de préserver les guillemets simples.

+0

Merci pour la procédure détaillée que je n'ai pas encore implémentée, car j'espérais toujours qu'il y ait un moyen plus facile (après tout, je devrais-je juste avoir des guillemets simples dans ma chaîne en yaml ...) Si je ne trouve pas de solution plus facile, je vais suivre la vôtre et revenir avec des commentaires. – 3yakuya

+1

Depuis que j'ai répondu à cette question, j'ai implémenté l'option 'preserve_quotes', j'ai mis à jour la réponse sur comment l'utiliser. – Anthon