Laden...

yaml-Dateien mit unbekannter Struktur NUR auf Gültigkeit prüfen

Erstellt von cScharping vor 6 Jahren Letzter Beitrag vor 6 Jahren 1.909 Views
C
cScharping Themenstarter:in
2 Beiträge seit 2018
vor 6 Jahren
yaml-Dateien mit unbekannter Struktur NUR auf Gültigkeit prüfen

Bin ein mittelmäßiger C# Programmierer.

Ich möchte in einem Ordner yaml-Dateien unbekannter Struktur validieren. Mir reicht also eine Methode, die für gültige yaml-Dateien true, für ungültige false zurückliefert. Der Inhalt der Dateien interessiert weiter nicht.

Ich habe per nuget die Bibliothek yamlDotNet ausprobiert, komm damit aber nicht klar. Da gibt es zwar ein Beispiel , das eine yaml-Datei validiert.

// By manipulating the list of node deserializers,
// it is easy to add behavior to the deserializer.
// This example shows how to validate the objects as they are deserialized.

using System;
using System.IO;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NodeDeserializers;

// First, we'll implement a new INodeDeserializer
// that will decorate another INodeDeserializer with validation:
public class ValidatingNodeDeserializer : INodeDeserializer
{
    private readonly INodeDeserializer _nodeDeserializer;

    public ValidatingNodeDeserializer(INodeDeserializer nodeDeserializer)
    {
        _nodeDeserializer = nodeDeserializer;
    }

    public bool Deserialize(EventReader reader, Type expectedType,
        Func<EventReader, Type, object> nestedObjectDeserializer,
        out object value)
    {
        if (_nodeDeserializer.Deserialize(reader, expectedType,
            nestedObjectDeserializer, out value))
        {
            var context = new ValidationContext(value, null, null);
            Validator.ValidateObject(value, context, true);
            return true;
        }
        return false;
    }
}

public class Program
{
	public static void Main()
	{
		// Then we wrap the existing ObjectNodeDeserializer
		// with our ValidatingNodeDeserializer:
		
		
		var deserializer = new Deserializer();

		var objectDeserializer = deserializer.NodeDeserializers
			.Select((d, i) => new {
				Deserializer = d as ObjectNodeDeserializer,
				Index = i
			})
			.First(d => d.Deserializer != null);
		
		deserializer.NodeDeserializers[objectDeserializer.Index] =
			new ValidatingNodeDeserializer(objectDeserializer.Deserializer);
		
		// This will fail with a validation exception
		deserializer.Deserialize<Data>(new StringReader(@"Name: ~"));
	}
}

public class Data
{
	[Required]
	public string Name { get; set; }
}

Das Beispiel setzt aber voraus, dass ich die "Key-Namen" der assoziativen Listen der yaml-Datei kenne. Die kenne ich bei meinen yaml-Dateien allerdings nicht.

Habt Ihr Tipps oder auch nur Stichwörter, die mir weiterhelfen?

T
2.224 Beiträge seit 2008
vor 6 Jahren

Das Problem ist, dass diese Komponente mit Serialisierung und Deserialisierung arbeitet.
Damit dies aber klappt, muss der Typ der aus der YAML Datei gelesen werden soll bekannt sein.
Entsprechend bringt dir diese Komponente nichts, da du eben den Content nicht kennst.

Im schlimmsten Fall müsstest du dir einen eigenen Parser basteln, der den Dateiaufbau wirklich sauber validieren kann ohne die Typen darin zu kennen.
Ob das aber klappt, kann ich mangels YAML Erfahrung nicht sagen.

Die Frage ist, was dies für Dateien sind und warum du diese validieren musst.
Kannst du dazu noch ein paar Informationen liefern?
Vielleicht lässt sich das Problem anderst lösen, wenn der Ursprung der Dateien klar ist.

Nachtrag:
Könnte vielleicht doch noch was werden mit der Komponente.
Folgendes Snippet gibt es noch bei den Beispielen.
Die ersten Zeilen sollten reichen.
Du musst im Bestfall nur schauen ob Load eine Exception wirft bei ungültigen Dokumenten.
Ist zwar kein schöner Ansatz, dürfte aber reichen um das Problem zu lösen.

using System;
using System.Text;
using System.IO;
using System.Collections.Generic;
using YamlDotNet.RepresentationModel;

namespace YamlDotNet.Samples
{
	public class LoadYamlStream
	{
		public void Main()
		{
			// Setup the input
			var input = new StringReader(Document);

			// Load the stream
			var yaml = new YamlStream();
			yaml.Load(input);

			// Examine the stream
			var mapping =
				(YamlMappingNode)yaml.Documents[0].RootNode;

			foreach (var entry in mapping.Children)
			{
				Console.WriteLine(((YamlScalarNode)entry.Key).Value);
			}

			// List all the items
			var items = (YamlSequenceNode)mapping.Children[new YamlScalarNode("items")];
			foreach (YamlMappingNode item in items)
			{
				Console.WriteLine(
					"{0}\t{1}",
					item.Children[new YamlScalarNode("part_no")],
					item.Children[new YamlScalarNode("descrip")]
				);
			}
		}

		private const string Document = @"---
            receipt:    Oz-Ware Purchase Invoice
            date:        2007-08-06
            customer:
                given:   Dorothy
                family:  Gale

            items:
                - part_no:   A4786
                  descrip:   Water Bucket (Filled)
                  price:     1.47
                  quantity:  4

                - part_no:   E1628
                  descrip:   High Heeled ""Ruby"" Slippers
                  price:     100.27
                  quantity:  1

            bill-to:  &id001
                street: |
                        123 Tornado Alley
                        Suite 16
                city:   East Westville
                state:  KS

            ship-to:  *id001

            specialDelivery:  >
                Follow the Yellow Brick
                Road to the Emerald City.
                Pay no attention to the
                man behind the curtain.
...";
	}
}

Nachtrag 2:
Wenn ich den Code richtig lese, sollte es reichen einen StreamReader pro Datei an die Load Methode zu geben.
Dann liest er die Dateien durch und sollte bei ungültigen Daten sogar knallen.
Entsprechend sollte die Lösung mit dem Snippet recht einfach sein.

Code könnte dann so aussehen.
Ist nur schnell in Notepad++ runtergetippt, musst du also selbst testen und prüfen ob es so passt 😃


public bool ParseFile(string filePath)
{
	using(StreamReader reader = new StreamReader(File.OpenRead(filePath))
	{
		try
		{
			// Load the stream
			var yaml = new YamlStream();
			yaml.Load(reader);
			
			// Dokumente sollten nicht leer sein!
			if(yaml.Documents.Count == 0)
				return false;

			// Examine the stream
			var mapping = (YamlMappingNode)yaml.Documents[0].RootNode;

			// Root Node hat keine Child Nodes, sollte auch nicht sein
			if(mapping.Children.Count == 0)
				return false;
			
			return true;
		}
		catch(Exception e)
		{
			// ToDo:
			// Exception bearbeiten
		}
		
		// Hier kommen wir nur hin, wenn wir eine Exception hatten!
		return false;
	}
}

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

C
cScharping Themenstarter:in
2 Beiträge seit 2018
vor 6 Jahren

Herzlichen Dank! Das funktioniert. Konnte in Deinem fix in Notepad++ runtergehackten Code nur in der Zeile
yaml.Load(reader);
keinen Streamreader übergeben. Mit StringReader ging es dann.

T
2.224 Beiträge seit 2008
vor 6 Jahren

@cScharping
Eigentlich gibt es eine Überladung, die einen TextReader erwartet.
Da StreamReader eine Ableitung von TextReader ist, sollte es eigentlich keine Probleme geben.
Oder gab es einen Compilerfehler?

Entsprechende Methode in YamlStream:


public void Load(TextReader input)

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.