I have made an XML schema to validate incoming xml files to validate if they are an Error or not.
I have some code that should return true when an xml file is valid, or false when it's not.
I have also made two XML files, one of which is valid, the other invalid. However, my code returns true for both of them, aka considers them both valid. How come? Where did I go wrong?
The schema:
XMLSchemaError.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="XMLSchemaError"
targetNamespace="http://tempuri.org/XMLSchemaError.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/XMLSchemaError.xsd"
xmlns:mstns="http://tempuri.org/XMLSchemaError.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name="Error">
<xs:complexType>
<xs:sequence>
<xs:element name="Server_ID" type="xs:integer" />
<xs:element name="Prioritizing" type="xs:token" />
<xs:element name="Type" type="xs:token" />
<xs:element name="Thrown_by" type="xs:token" />
<xs:element name="Code" type="xs:string" />
<xs:element name="Text" type="xs:string" />
<xs:element name="Time_occured" type="xs:dateTime" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Code to validate the XML file:
XMLValidation.cs
private bool isValid = false;
public bool checkValid(string fileName)
{
try
{
var reader = XmlReader.Create("../../XMLFiles/" + fileName);
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.Load(reader);
doc.Schemas.Add(null, "../../Model/XMLSchemaError.xsd");
doc.Validate(ValidationCallBack);
isValid = true;
}
catch
{
isValid = false;
}
System.Diagnostics.Debug.WriteLine("Check: " + isValid);
return isValid;
}
private void ValidationCallBack(object sender, ValidationEventArgs e)
{
throw new Exception();
}
The 2 XML files to validate:
Valid.xml
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Server_ID>4</Server_ID>
<Prioritizing>major</Prioritizing>
<Type>test error</Type>
<Thrown_by>test program</Thrown_by>
<Code></Code>
<Text></Text>
<Time_occurred>2016-02-9T12:34:56.7890</Time_occurred>
</Error>
Invalid.xml
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
As commenter Damien_The_Unbeliever points out, since your XML doesn't claim to contain any elements that are validated by the schema in question, both documents trivially pass validation. There's nothing in them to validate.
There are a variety of ways to correct the situation. The simplest is to just add a default XML namespace to the root element. E.g.:
<?xml version="1.0" encoding="UTF-8"?>
<note xmlns="http://tempuri.org/XMLSchemaError.xsd">
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
Having done that, the above XML now will fail validation according to your schema.
If you were to do the same thing to the "valid" XML, you would find that it too fails validation. This is for two reasons:
The schema misspells the word "occurred", while the XML does not. This can be fixed either by correcting the spelling the schema or by using the misspelling in the XML.
The XML has an incorrectly formatted dateTime value. The day-of-month field is specified using only a single digit, but the XML format requires two digits for both the month and day-of-month fields.
An actual valid XML, given the schema you've provided, would look like this:
<?xml version="1.0" encoding="UTF-8"?>
<Error xmlns="http://tempuri.org/XMLSchemaError.xsd">
<Server_ID>4</Server_ID>
<Prioritizing>major</Prioritizing>
<Type>test error</Type>
<Thrown_by>test program</Thrown_by>
<Code></Code>
<Text></Text>
<Time_occured>2016-02-09T12:34:56.7890</Time_occured>
</Error>
(I've opted to misspell the element name in the XML, rather than fixing the schema, just because it simplifies the example XML required in this answer :) ).
Related
I have following xml schema
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" attributeFormDefault="unqualified" elementFormDefault="qualified"
targetNamespace="http://www.MySchema.net" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="RootElement">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:schema>
and following xml's are validated against above schema:
Case 1: (Schema exception)
<?xml version="1.0" encoding="utf-8" ?> <RootElement11 name="Configuration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.MySchema.net Root" xmlns="http://www.MySchema.net">
</RootElement11>
Case 2: (No exception)
<?xml version="1.0" encoding="utf-8" ?>
<RootElement11 name="Configuration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.YourSchema.net Root" xmlns="http://www.YourSchema.net">
</RootElement11>
Case 3: (No exception)
<RootElement11 name="Configuration">
</RootElement11>
For Case 1, I get an expected exception that "The 'http://www.MySchema.net:RootElement1' element is not declared.", but Case 2 and Case 3 are validated without exception.
I wanted to know if there is a possibility to throw an exception when xml files with false namespaces or without namespaces are validated using XDocument.Validate method.
I found some info which use XmlReader with settings to throw these type of exception. I see two possibilites 1) Get back to XmlReader from XDocument, 2) Validate using XmlReader and use XDocument to do LINQ queries.
But is it possible to accomplish this without XmlReader.
The issue is that both cases 2 and 3 are valid per the schema - your schema doesn't have any opinion on elements in namespaces other than its targetNamespace.
XmlReader can return a warning for this, but there's no overload for XDocument that will do it. The snippet in your linked question uses an XmlReader wrapper around XDocument, I can't see why you'd have any issue with doing it this way.
My problem is that i dont know how to properly write a xsd that allows me to validate xml in such way :
i need to have few necessary nodes (in any order) and to allow any other nodes to be in root
so for example i need to validate such xml, with 2 necessary nodes:
<root>
<necessary1/>
<someRandomNode1/>
<necessary2/>
<someRandomNode2/>
<someRandomNode3/>
</root>
but this has to be in any order and <xs:any/> is probably not what im looking for.
edit:
this 'someRandomNodeX' is not name of node, it can be everything. Number of this unscpecified nodes is unknown too.
There is a solution if the required elements and the non-required elements can be made in different namespaces. It requires XML Schema 1.1.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" vc:minVersion="1.1"
xmlns:namespace="http://www.example.com/"
targetNamespace="http://www.example.com/">
<xs:element name="root">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="necessary1" type="xs:string"/>
<xs:element name="necessary2" type="xs:string"/>
<xs:any namespace="##other" processContents="lax"/>
<xs:any namespace="##local" processContents="lax"/>
</xs:choice>
<xs:assert test="exactly-one(namespace:necessary1) and exactly-one(namespace:necessary2)"/>
</xs:complexType>
</xs:element>
</xs:schema>
This validates:
<?xml version="1.0" encoding="UTF-8"?>
<namespace:root
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.example.com test.xsd"
xmlns:namespace="http://www.example.com/">
<namespace:necessary1/>
<someRandomNode1/>
<namespace:necessary2/>
<someRandomNode2/>
<someRandomNode3/>
</namespace:root>
As a developer with a good deal of XML consuming and producing experience, I've never really interacted with schemas before. For the first time this is actually occurring for me.
I've run across a "feature" that I consider more of a bug which is well documented.
When using XDocument.Validate() it seems that there are cases in which the document will be valid if it doesn't match the schema specified. I feel this is most likely a flaw in my understanding of the relationship between XSDs, XML namespaces, and expected validation processes.
Therefore I submit to you my XML sample, my XSD sample, and my validation code.
XML - this is INTENTIONALLY the wrong document.
<?xml version="1.0" encoding="utf-8" ?>
<SuppliesDefinitions
xmlns="http://lavendersoftware.org/schemas/SteamGame/Data/Xml/Supplies.xsd">
<Supply type="Common">
<Information/>
<Ritual/>
<Weapon/>
<Tool count="1"/>
<Tool count="2"/>
<Tool count="3"/>
</Supply>
<Supply type="Uncommon">
<Information/>
<Weapon/>
<Tool count="1"/>
<Tool count="2"/>
<Tool count="3"/>
<Tool count="4"/>
</Supply>
<Supply type="Rare">
<Information/>
<Rune/>
<Weapon/>
<Tool count="2"/>
<Tool count="3"/>
<Tool count="4"/>
</Supply>
</SuppliesDefinitions>
The XSD used to validate it. (Again, this is intentionally the WRONG document for the above XML)
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Encounters"
targetNamespace="http://lavendersoftware.org/schemas/SteamGame/Data/Xml/Encounters.xsd"
elementFormDefault="qualified"
xmlns="http://lavendersoftware.org/schemas/SteamGame/Data/Xml/Encounters.xsd"
xmlns:mstns="http://lavendersoftware.org/schemas/SteamGame/Data/Xml/Encounters.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:complexType name="ToolType">
<xs:attribute name="count" use="required" type="xs:int"/>
</xs:complexType>
<xs:complexType name="TaskType">
<xs:choice maxOccurs="unbounded" minOccurs="1">
<xs:element name="Weapon"/>
<xs:element name="Information"/>
<xs:element name="Tool" type="ToolType"/>
<xs:element name="Ritual"/>
</xs:choice>
</xs:complexType>
<xs:complexType name="EncounterType">
<xs:sequence maxOccurs="unbounded" minOccurs="1">
<xs:element name="Task" type="TaskType"/>
</xs:sequence>
<xs:attribute name="name" use="required" type="xs:string"/>
</xs:complexType>
<xs:element name="EncounterDefinitions">
<xs:complexType>
<xs:sequence maxOccurs="unbounded" minOccurs="1">
<xs:element name="Encounter" type="EncounterType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
And finally the validation code.
private static void ValidateDocument(XDocument doc)
{
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(null, XmlReader.Create(new StreamReader(XmlSchemaProvider.GetSchemaStream("Encounters.xsd"))));
doc.Validate(schemas, (o, e) =>
{
//This is never hit!
Console.WriteLine("{0}", e.Message);
Assert.False(e.Severity == XmlSeverityType.Error);
});
}
I was wondering if someone can explain what I am doing wrong. I feel I'm making some incorrect assumptions about the way this SHOULD be working. It seems to me using one xsd against a completely unrelated XML document would be invalid.
There is no nodes in your XML that can be validated by the schema (namespaces are different). As result it does not report any errors. As far as I know behavior for nodes that are not matched to any schema is allow anything.
You also could set validation options in XmlReaderSettings to allow warnings:
ReportValidationWarnings - Indicates that events should be reported if a validation warning occurs. A warning is typically issued when there is no DTD or XML Schema to validate a particular element or attribute against. The ValidationEventHandler is used for notification.
Check out XmlSchemaSet.Add and HOW TO: Validate an XML Document by Using Multiple Schemas if you expect nodes from multiple namespaces to be present in the XML.
Here is an example from a xsd and a xml
XML
<?xml version="1.0" encoding="UTF-8"?>
<config test2="true">
</config>
XSD
<?xml version="1.0" encoding="utf-8"?>
<xs:schema targetNamespace="rules.xsd" xmlns="rules.xsd" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msprop="urn:schemas-microsoft-com:xml-msprop">
<xs:element name="config">
<xs:complexType>
<xs:attribute name="test1" type="xs:boolean" default="false" />
<xs:attribute name="test2" type="xs:string" default="mary123" />
</xs:complexType>
</xs:element>
</xs:schema>
I can use this xsd to validate this xml in C# using this block of code
XmlDocument doc = new XmlDocument();
XmlTextReader schemaReader = new XmlTextReader(System.IO.Path.GetFullPath("Mi_XSD_here.xsd"));
XmlSchema schema = XmlSchema.Read(schemaReader, ValidationCallBack);
doc.Schemas.Add(schema);
doc.Load("Mi_XML_here.xml");
doc.Validate(ValidationCallBack);
The problem is: I have two default attributes in xsd, but when i run this code he doesn't insert the attributes in XmlDocument, the result is the same xml that i passed to the system.
The default attributes are not working and i cant figure why they aren't working, i do believe that exists other forms to solve this problem, i did find this but didn't worked
Extensions.Validate Method
obs: ValidationCallBack is some return on error function that i think isn't related to the problems
Your schema's target namespace is rules.xsd so your xml file needs to include that to validate against the schema. Also I'm assuming teste2 is a typo (since that doesn't conform to the schema) and you meant test2:
<config test2="" xmlns="rules.xsd" />
In this case only test1 will get added with a default since test2 is already set to an empty string.
i'm trying to learn a little bit XSD and I'm trying to create a XSD for this xml:
<Document>
<TextBox Name="Username" />
<TextBox Name="Password" />
</Document>
... so there's an element, which is an abstract complex type. Every element have elements and so on. Document and TextBox are extending Element.
I trid this:
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Document">
<xs:complexType>
<xs:complexContent>
<xs:extension base="Element">
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType name="Element" abstract="true">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Element" type="Element"></xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="TextBox">
<xs:complexContent>
<xs:extension base="Element">
<xs:attribute name="Name" type="xs:string" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
I compiled it to C# with Xsd2Code, and now I try to deserialize it:
var serializer = new XmlSerializer(typeof(Document));
var document = (Document)serializer.Deserialize(new FileStream("Document1.xml", FileMode.Open));
foreach (var element in document.Element1)
{
Console.WriteLine(((TextBox)element).Name);
}
Console.ReadLine();
and it dosen't print anything. When I try to serialize it like so:
var serializer = new XmlSerializer(typeof(Document));
var document = new Document();
document.Element1 = new List<Element>();
document.Element1.Add(new TextBox()
{
Name = "abc"
});
serializer.Serialize(new FileStream("d.xml", FileMode.Create), document);
...the output is:
<?xml version="1.0"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Element1>
<Element xsi:type="TextBox">
<Element1 />
<Name>abc</Name>
</Element>
</Element1>
</Document>
When it should be something like:
<?xml version="1.0"?>
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<TextBox Name="abc" />
</Document>
Any ideas how to fix the xsd or another code generator?
Thanks.
In the XSD code, look at the line where you declare an element named "Element". This element should be named as such <xs:element name="TextBox" /> if you want to use <TextBox/> in your XML files.
With the above change implemented, you could run xsd.exe /c YourFile.xsd to generate a C# class from the XSD. Include the generated file in your C# project and you'll be able to use it like this:
Document d = new Document();
d.TextBox = new TextBox[]
{
new TextBox() { Name = "Username" },
new TextBox() { Name = "Password" },
};
XmlSerializer ser = new XmlSerializer(typeof(Document));
ser.Serialize(Console.Out, d);
You'll also be able to deserialize the XML back into C# objects.
<Document xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TextBox xsi:type="TextBox" Name="Username" />
<TextBox xsi:type="TextBox" Name="Password" />
</Document>
Note the xsi:type attribute - because you declared your TextBox element to be of type Element you will have to provide which concrete type implementation to use when deserializing one of these elements. Out of curiosity I tried changing the XSD type of the element named TextBox to the TextBox type, but xsd.exe threw a StackOverflowException. I chuckled. It probably to do with the recursive type relationship between Element and TextBox, but maybe a different tool would handle it differently?