2017-03-21 1 views
0

Nous construisons un service. Il doit lire la configuration d'un fichier. Nous utilisons actuellement YAML et Jackson pour désérialiser le YAML. Nous avons une situation où notre fichier YAML doit hériter/étendre un autre fichier YAML. Par exemple, quelque chose comme:Bibliothèque YAML ou JSON prenant en charge l'héritage

extends: base.yaml 

appName: my-awesome-app 

... 

partie donc de la configuration est stockée dans base.yaml. Y a-t-il une bibliothèque qui a un support pour cela? Points bonus si cela permet d'hériter de plus d'un fichier. Nous pourrions changer en utilisant JSON au lieu de YAML.

+0

[HOCON] (https://github.com/typesafehub/config#features-of-hocon) a la syntaxe 'include' –

Répondre

1

Ni JSON ni YAML ne peuvent inclure des fichiers. Quoi que vous fassiez sera une étape de pré-traitement où vous mettrez le base.yaml et votre fichier réel ensemble.

Une façon grossière de le faire serait:

#include base.yaml 
appName: my-awesome-app 

Que ce soit votre fichier. Au chargement, vous lisez d'abord la première ligne, et si elle commence par #include, vous le remplacez par le contenu du fichier inclus. Vous devez le faire de manière récursive. C'est essentiellement ce que fait le préprocesseur C avec les fichiers C et inclut.

sont Désavantages:

  • même si les deux fichiers sont valides YAML, le résultat ne peut pas.
  • Si l'un des fichiers contient une fin de directive ou un marqueur de fin de document (--- ou ...), vous obtiendrez deux documents distincts dans un fichier.
  • Vous ne pouvez pas remplacer les valeurs de base.yaml dans votre fichier.

Donc, une alternative serait d'opérer réellement sur la structure YAML. Pour cela, vous avez besoin de l'API de l'analyseur YAML (SnakeYAML dans votre cas) et analyser votre fichier avec cela. Vous devez utiliser l'API Compose:

private Node preprocess(final Reader myInput) { 
    final Yaml yaml = new Yaml(); 
    final Node node = yaml.compose(myInput); 
    processIncludes(node); 
    return node; 
} 

private void processIncludes(final Node node) { 
    if (node instanceof MappingNode) { 
     final List<NodeTuple> values = ((MappingNode) node).getValue(); 
     for (final NodeTuple tuple: values) { 
      if ("!include".equals(tuple.getKeyNode().getTag().getValue())) { 
       final String includedFilePath = 
         ((ScalarNode) tuple.getValueNode()).getValue(); 
       final Node content = preprocess(new FileReader(includedFilePath)); 
       // now merge the content in your preferred way into the values list. 
       // that will change the content of the node. 
      } 
     } 
    } 
} 

public String executePreprocessor(final Reader source) { 
    final Node node = preprocess(source); 
    final StringWriter writer = new StringWriter(); 
    final DumperOptions dOptions = new DumperOptions() 
    Serializer ser = new Serializer(new Emitter(writer, dOptions), 
            new Resolver(), dOptions, null); 
    ser.open(); 
    ser.serialize(node); 
    ser.close(); 
    return writer.toString(); 
} 

Ce code parser comprend comme ceci:

!include : base.yaml 
appName: my-awesome-app 

J'utilisé la balise privée !include de sorte qu'il n'y aura pas des conflits de noms avec une touche de cartographie normale. Attention à l'espace derrière !include. Je n'ai pas donné de code pour fusionner le fichier inclus car je ne savais pas comment gérer les clés de mappage en double. Il ne devrait pas être difficile à mettre en œuvre cependant. Attention aux bugs, je n'ai pas testé ce code.

La chaîne résultante peut être l'entrée de Jackson.