Erste Schritte mit XSD-Validierung mit .NET gestartet
-
22-09-2019 - |
Frage
Hier ist mein erster Versuch XML Validierung mit XSD.
Die XML-Datei validiert werden:
<?xml version="1.0" encoding="utf-8" ?>
<config xmlns="Schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">
<levelVariant>
<filePath>SampleVariant</filePath>
</levelVariant>
<levelVariant>
<filePath>LegendaryMode</filePath>
</levelVariant>
<levelVariant>
<filePath>AmazingMode</filePath>
</levelVariant>
</config>
Die XSD, befindet sich in "Schemen / config.xsd" in Bezug auf die XML-Datei validiert werden:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="config">
<xs:complexType>
<xs:sequence>
<xs:element name="levelVariant">
<xs:complexType>
<xs:sequence>
<xs:element name="filePath" type="xs:anyURI">
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Im Moment möchte ich nur die XML-Datei zu validieren, genau wie es derzeit scheint. Sobald ich dies besser zu verstehen, werde ich mehr erweitern. Muss ich so viele Zeilen für etwas so Einfaches wie die XML-Datei wirklich, wie es existiert zur Zeit?
Der Validierungscode in C #:
public void SetURI(string uri)
{
XElement toValidate = XElement.Load(Path.Combine(PATH_TO_DATA_DIR, uri) + ".xml");
// begin confusion
// exception here
string schemaURI = toValidate.Attributes("xmlns").First().ToString()
+ toValidate.Attributes("xsi:noNamespaceSchemaLocation").First().ToString();
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null, schemaURI);
XDocument toValidateDoc = new XDocument(toValidate);
toValidateDoc.Validate(schemas, null);
// end confusion
root = toValidate;
}
Sie den obigen Code Lauf gibt diese Ausnahme:
The ':' character, hexadecimal value 0x3A, cannot be included in a name.
Jede Beleuchtung würde geschätzt.
Lösung
Anstatt die XDocument.Validate Erweiterungsmethode, würde ich eine XmlReader , die so konfiguriert werden kann, ein Inline-Schema über XmlReaderSettings . Sie könnten einige etwas wie den folgenden Code tun.
public void VerifyXmlFile(string path)
{
// configure the xmlreader validation to use inline schema.
XmlReaderSettings config = new XmlReaderSettings();
config.ValidationType = ValidationType.Schema;
config.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
config.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
config.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
config.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
// Get the XmlReader object with the configured settings.
XmlReader reader = XmlReader.Create(path, config);
// Parsing the file will cause the validation to occur.
while (reader.Read()) ;
}
private void ValidationCallBack(object sender, ValidationEventArgs vea)
{
if (vea.Severity == XmlSeverityType.Warning)
Console.WriteLine(
"\tWarning: Matching schema not found. No validation occurred. {0}",
vea.Message);
else
Console.WriteLine("\tValidation error: {0}", vea.Message);
}
Der obige Code übernimmt die folgenden Anweisungen.
using System.Xml;
using System.Xml.Schema;
Sie einfach diese einfach zu halten ich keine boolean
oder eine Sammlung von Validierungsfehler zurückgekommen, könnte man leicht diese ändern, dies zu tun.
Hinweis: Ich änderte Ihre config.xml und config.xsd sie zu validieren zu bekommen. Dies sind die Änderungen, die ich gemacht.
config.xsd:
<xs:element maxOccurs="unbounded" name="levelVariant">
config.xml:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">
Andere Tipps
Im Anschluss ist aus einer Arbeitsprobe:
Verwendung:
XMLValidator val = new XMLValidator();
if (!val.IsValidXml(File.ReadAllText(@"d:\Test2.xml"), @"D:\Test2.xsd"))
MessageBox.Show(val.Errors);
Klasse:
public class CXmlValidator
{
private int nErrors = 0;
private string strErrorMsg = string.Empty;
public string Errors { get { return strErrorMsg; } }
public void ValidationHandler(object sender, ValidationEventArgs args)
{
nErrors++;
strErrorMsg = strErrorMsg + args.Message + "\r\n";
}
public bool IsValidXml(string strXml/*xml in text*/, string strXsdLocation /*Xsd location*/)
{
bool bStatus = false;
try
{
// Declare local objects
XmlTextReader xtrReader = new XmlTextReader(strXsdLocation);
XmlSchemaCollection xcSchemaCollection = new XmlSchemaCollection();
xcSchemaCollection.Add(null/*add your namespace string*/, xtrReader);//Add multiple schemas if you want.
XmlValidatingReader vrValidator = new XmlValidatingReader(strXml, XmlNodeType.Document, null);
vrValidator.Schemas.Add(xcSchemaCollection);
// Add validation event handler
vrValidator.ValidationType = ValidationType.Schema;
vrValidator.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);
//Actual validation, read conforming the schema.
while (vrValidator.Read()) ;
vrValidator.Close();//Cleanup
//Exception if error.
if (nErrors > 0) { throw new Exception(strErrorMsg); }
else { bStatus = true; }//Success
}
catch (Exception error) { bStatus = false; }
return bStatus;
}
}
Der obige Code validiert folgende xml (code3) gegen XSD (code4).
<!--CODE 3 - TEST1.XML-->
<address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test1.xsd">
<name>My Name</name>
<street>1, My Street Address</street>
<city>Far</city>
<country>Mali</country>
</address>
<!--CODE 4 - TEST1.XSD-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Bei der Validierung gegen Ihre xml / xsd ich von Fehlern erhalten anders als Ihr; Ich denke, das kann Ihnen helfen, (Hinzufügen / Entfernen XML-Elemente) weiterhin von hier:
Sie können auch den umgekehrten Prozess versuchen; versuchen, das Schema von Ihrem xml und vergleichen Sie mit Ihrem tatsächlichen xsd Erzeugung - sehen Sie den Unterschied; und der einfachste Weg, dies zu tun ist, generieren zu verwenden Schema VS IDE. Es folgt, wie würden Sie das tun:
Hope, das hilft.
- EDIT -
Dies ist auf Johns Wunsch, bitte aktualisierten Code unter Verwendung von nicht veralteten Methoden finden Sie unter:
public bool IsValidXmlEx(string strXmlLocation, string strXsdLocation)
{
bool bStatus = false;
try
{
// Declare local objects
XmlReaderSettings rs = new XmlReaderSettings();
rs.ValidationType = ValidationType.Schema;
rs.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ReportValidationWarnings;
rs.ValidationEventHandler += new ValidationEventHandler(rs_ValidationEventHandler);
rs.Schemas.Add(null, XmlReader.Create(strXsdLocation));
using (XmlReader xmlValidatingReader = XmlReader.Create(strXmlLocation, rs))
{ while (xmlValidatingReader.Read()) { } }
////Exception if error.
if (nErrors > 0) { throw new Exception(strErrorMsg); }
else { bStatus = true; }//Success
}
catch (Exception error) { bStatus = false; }
return bStatus;
}
void rs_ValidationEventHandler(object sender, ValidationEventArgs e)
{
if (e.Severity == XmlSeverityType.Warning) strErrorMsg += "WARNING: " + Environment.NewLine;
else strErrorMsg += "ERROR: " + Environment.NewLine;
nErrors++;
strErrorMsg = strErrorMsg + e.Exception.Message + "\r\n";
}
Verwendung:
if (!val.IsValidXmlEx(@"d:\Test2.xml", @"D:\Test2.xsd"))
MessageBox.Show(val.Errors);
else
MessageBox.Show("Success");
test2.xml
<?xml version="1.0" encoding="utf-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test2.xsd">
<levelVariant>
<filePath>SampleVariant</filePath>
</levelVariant>
<levelVariant>
<filePath>LegendaryMode</filePath>
</levelVariant>
<levelVariant>
<filePath>AmazingMode</filePath>
</levelVariant>
</config>
Test2.XSD (generiert aus VS IDE)
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="config">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="levelVariant">
<xs:complexType>
<xs:sequence>
<xs:element name="filePath" type="xs:anyURI">
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Dies ist garantiert an die Arbeit!
Ihr Code das Schema Lage zu extrahieren sieht seltsam. Warum bekommen Sie den Wert des Attributs xmlns und verketten es mit dem Wert des xsi: noNamespaceSchemaLocation Attribut? Die Ausnahme wird durch die Tatsache, dass Sie nicht ein Präfix in einem Aufruf Attribute angeben können; Sie müssen die gewünschten XNamespace angeben.
Versuchen Sie, diese (nicht getestet):
// Load document
XDocument doc = XDocument.Load("file.xml");
// Extract value of xsi:noNamespaceSchemaLocation
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
string schemaURI = (string)doc.Root.Attribute(xsi + "noNamespaceSchemaLocation");
// Create schema set
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("Schemas", schemaURI);
// Validate
doc.Validate(schemas, (o, e) =>
{
Console.WriteLine("{0}", e.Message);
});