Validate XML nodes against a schema using accesory XSD files - c#

I need to create XML file compliant to a complex XSD file. My approach is to validate step by step (using TDD) every type on the XSD. I can only validate root elements, so what I am doing is
Create a new schema, on the same namespace, with only a root element called "TestNode" of the target complex type to test
Add the schema to the schema validation list
So, this a part of the original complex schema
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema xmlns="urn:FooSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:FooSchema">
<xs:element name="Document" type="Document"/>
<xs:complexType name="AComplexType">
<xs:sequence>
<xs:element name="MsgId" type="Max35Text"/>
...
</xs:complexType>
...
...
<xs:element name="OriginalElement" type="AComplexType"/>
...
...
</xs:schema>
I want to create and validate XML nodes with "AComplexTypeFormat" format, but I can't validate 'OriginalElement' because it's not a root element.
I am not allowed to modify the original XSD file. Thus, my workaround is create an accesory NodeTester XSD file:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element xmlns="urn:FooSchema" name="TestNode" type="AComplexType" />
</xs:schema>
And then add BOTH schemas to the validation list
XmlSchema originalSchema = XmlSchema.Read(new StreamReader("ComplexSchema.xsd"), XMLValidationEventHandler);
XmlSchema testingSchema = XmlSchema.Read(new StreamReader("NodeTester.xsd"), XMLValidationEventHandler);
XmlReaderSettings validatorSettings = new XmlReaderSettings();
validatorSettings.Schemas.Add(originalSchema);
validatorSettings.Schemas.Add(testingSchema);
validatorSettings.ValidationType = ValidationType.Schema;
validatorSettings.ConformanceLevel = ConformanceLevel.Fragment;
validatorSettings.ValidationEventHandler += new ValidationEventHandler(XMLValidationEventHandler);
XmlReader validationReader = XmlReader.Create(new StringReader(stringXML), validatorSettings);
while (validationReader.Read()) { }
When I validate the stringXML that contains the XML node, I receive an error (sorry, translating from spanish):
The element 'TestNode' has a secondary element 'MsgId' not valid. Expected list of possible elements: 'MsgId' in the namespace 'urn:FooSchema'.
Cryptic :( 'MsgID' is not allowed because the validator is waiting for... 'MsgID'
What is wrong? Is this correct (adding another scheme to list), or there is another way to merge elements of the same namespace coming from different files to make a validation?
Thanks!

Finally, I have found another workaround: not add new schemas, but EDIT THE SCHEME
So, If I need to test if a XML fragment(node) is compilant to a particular XSD type, I just edit the schema and add a ROOT element of the type to test, with the name of the XML fragment root element.
public static void AddElementToSchema(XmlSchema xmlSchema, string elementName, string elementType, string xmlNamespace)
{
XmlSchemaElement testNode = new XmlSchemaElement();
testNode.Name = elementName;
testNode.Namespaces.Add("", xmlNamespace);
testNode.SchemaTypeName = new XmlQualifiedName(elementType, xmlNamespace);
xmlSchema.Items.Add(testNode);
xmlSchema.Compile(XMLValidationEventHandler);
}
In my new, compiled in memory schema, the element is now root and I can validate it correctly :)

Related

Parsing XML file with xmlns tag defined but not using namespaces

I am struggling with parsing XPathDocument with XPathNavigator where XML document contains xmlns tag, but no namesapce prefix is used.
<?xml version="1.0" encoding="UTF-8"?>
<Order version="1" xmlns="http://example.com">
<Data>
<Id>1</Id>
<b>Aaa</b>
</Data>
</Order>
SelectSingleNode method requires namespace manager with namespace defined.
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nav.NameTable);
nsmgr.AddNamespace("ns","http://example.com");
As a result I have to start using ns prefix in all XPath expressions, although the original XML file does not use prefixes, for e.g.:
nav.SelectSingleNode("/ns:Order/ns:Data/ns:Id", nsmgr)
This adds an unnecessary complexity.
Is there any way to push XPathNavigator to ignore namespace and its prefixes? Ot at least use of prefixes, as namespace definition itself exists only once, so not much effort
The XML has a default namespace. So each and every XML element is bound to it, visible or not.
It is better to use LINQ to XML.
It is a preferred XML API, and available in the .Net Framework since 2007.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<Order version='1' xmlns='http://example.com'>
<Data>
<Id>1</Id>
<b>Aaa</b>
</Data>
</Order>");
XNamespace ns = xdoc.Root.GetDefaultNamespace();
foreach (XElement elem in xdoc.Descendants(ns + "Data"))
{
Console.WriteLine("ID: {0}, b: {1}"
, elem.Element(ns + "Id")?.Value
, elem.Element(ns + "b")?.Value);
}
}

XsdDataContractExporter will not compile an Xsd schema with an <xs:attribute /> element

I have a WCF service with a basicHttpBinding endpoint. One of the types included in one of the data contracts implements IXmlSerializable and includes an XmlSchemaProvider that returns the contents of an Xsd file embedded in the assembly. When I hit the endpoint in the browser, the Wsdl generation fails, "XmlSchemaException: The 'http://www.w3.org/XML/1998/namespace:lang' attribute is not declared." There is an issue compiling the embedded Xsd file.
I did some research and discovered that the DataContractSerializer (and XsdDataContractExporter) do not support all serializable types (http://msdn.microsoft.com/en-us/library/ms731923(v=vs.110).aspx). I suspect this behavior is at work here because the embedded Xsd includes a required attribute.
I also dotPeeked the XsdDataContractExporter code and found that get_Schemas compiles the imported schema set before returning and this is what is failing.
As a workaround, I modified the WCF service to use XmlSerializer instead of DataContractSerializer and the Wsdl generates OK. Has anyone experienced this behavior before? Thoughts?
Here is a console application and sample Xsd that will reproduce the error.
using System;
using System.Resources;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace SchemaCompiler
{
public class Program
{
static void Main()
{
var exporter = new XsdDataContractExporter();
if (!exporter.CanExport(typeof(Foo)))
return;
exporter.Export(typeof(Foo));
// The following line will throw XmlSchemaException: The 'http://www.w3.org/XML/1998/namespace:lang' attribute is not declared.
var schemaSet = exporter.Schemas;
Console.WriteLine(schemaSet.IsCompiled);
}
[Serializable]
[XmlSchemaProvider("GetSchema")]
public class Foo : IXmlSerializable
{
private static XmlSchema _xmlSchema;
public String Bar { get; set; }
public String Language { get; set; }
public static XmlQualifiedName GetSchema(XmlSchemaSet schemaSet)
{
var type = typeof(Foo);
var xsdFileStream = type.Assembly.GetManifestResourceStream(type, "Foo.xsd");
if (xsdFileStream == null) throw new MissingManifestResourceException(
"The embedded resource Foo.xsd was not found. For more information, http://support.microsoft.com/kb/319292");
_xmlSchema = XmlSchema.Read(xsdFileStream, null);
schemaSet.Add(_xmlSchema);
return new XmlQualifiedName(type.Name, "http://tempuri.org/Foo.xsd");
}
#region IXmlSerializable Members
public XmlSchema GetSchema()
{
return _xmlSchema;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
throw new NotImplementedException();
}
#endregion
}
}
}
And here is the sample Xsd file that you will need to embed in the assembly:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Foo" xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://tempuri.org/Foo.xsd" elementFormDefault="qualified">
<xs:import namespace="http://www.w3.org/XML/1998/namespace" />
<xs:complexType name="Foo" mixed="false">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="unbounded" name="Value">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute ref="xml:lang" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:schema>
Your analysis may be correct. But the error message complains about an undeclared attribute, not about an unsupported construct, so I wonder if there is another explanation.
Your schema document should be fine if the software that uses it knows where to find a schema document for the namespace http://www.w3.org/XML/1998/namespace. But most XSD software I am familiar with doesn't have built-in knowledge of that namespace. I wonder if your initial setup would work better if you changed the import to read something like this:
<xs:import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
N.B. W3C serves schemas and DTDs very very slowly (it's a long story), so you may prefer (even just for testing, and certainly for serious work) to make a local copy of the schema document for the XML namespace (the URI is given in the example) and point to your local copy instead.

How to do simple XML schema validation with no namespaces in C#

I have generated a set of classes using xsd.exe and created an XML document from the resulting generated code. I would now like to validate the serialized class instance against the original xsd.
My XML is like this:
<?xml version="1.0" encoding="UTF-8"?>
<MyRoot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-- rest of XML document here
</MyRoot>
My XSD is like this:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="MyRoot" type="MyRootType"/>
-- MyRootType definition and rest of XSD
</xs:schema>
When I try to validate the XML using a XmlReader, I get the following error:
"The 'MyRoot' element is not declared."
What could be wrong?
In your MyRoot element, you need to add the location of the XSD. I would also recommend defining the namespace (unless you have good reason not to).
<api:MyRoot xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:api='http://www.myserver.com/schema'
xsi:schemaLocation='http://www.myserver.com/schema http://www.myserver.com/schema/websuiterecord.xsd'>
</api:MyRoot>
This way, the validation tool knows where to find your XSD to validate your XML against.
The approach was correct, but the XSD was not infact being read. I corrected this and it worked as expected.

creating class object with included schema using xsd.exe

I have received a XSD schema MainSchema.XSD and also Common.Xsd schema.
In MainSchema.xsd i have a following line:
<xs:include schemaLocation="Common.xsd"/>
And Common.Xsd hold a definition for various types of data like:
<xs:simpleType name="SSN">
<xs:annotation>
<xs:documentation>Social security number is 10 digits</xs:documentation>
<xs:appinfo>
<altova:exampleValues>
<altova:example value="5412983209"/>
<altova:example value=""/>
</altova:exampleValues>
</xs:appinfo>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:whiteSpace value="collapse"/>
<xs:pattern value="([0-9]{10})?"/>
</xs:restriction>
</xs:simpleType>
and in MainSchema i have a property called SSNField of type SSN:
<xs:attribute name="CompanySSN" type="SSN">
<xs:annotation>
<xs:documentation>SSN number of Company</xs:documentation>
</xs:annotation>
</xs:attribute>
When i create a c# object class with this command:
xsd.exe -c -l:c# MainSchema.xsd Common.Xsd
it then created a object called:
MainSchema_Common.cs
When i validate an object against this Schema it comes up with an Exception:
{"Type 'http://schemas.domain.com:SSN' is not declared, or is not a simple type."}
Does anyone knows what i´m doing wrong ?
Bear in mynd that i received this XSD schemas from a outside source and i was told that there were no errors in this files.
Sincerly
agh
You need to explain how you are validating. I assume you are creating an instance of the class and then serializing to XML, and the xml is not validating?
You need to be aware that just because your xml is the product of serializing a type derived using xsd.exe does not automatically mean the xml will be compliant to the schema.
You may need to prime the XmlSerializer by injecting an override for the root namespace or indeed other nodes in the document.
For example to inject a namespace at a certain node:
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
var elementAttribute = new XmlElementAttribute(typeof(SSN))
{
ElementName = "SSN",
Namespace = "http://schemas.domain.com:SSN"
};
var newAttribute = new XmlAttributes();
newAttribute.XmlElements.Add(elementAttribute);
overrides.Add(typeof(ParentNodeType), "SSN", newAttribute);
To call the serilaizer:
XmlSerializer serializer = new XmlSerializer(typeof(MyType), overrides);
Hope this helps

How to generate .NET 4.0 classes from xsd?

What are the options to generate .NET 4.0 c# classes (entities) from an xsd file, using Visual Studio 2010?
simple enough; just run (at the vs command prompt)
xsd your.xsd /classes
(which will create your.cs). Note, however, that most of the intrinsic options here haven't changed much since 2.0
For the options, use xsd /? or see MSDN; for example /enableDataBinding can be useful.
xsd.exe as mentioned by Marc Gravell. The fastest way to get up and running IMO.
Or if you need more flexibility/options :
xsd2code VS add-in (Codeplex)
I show you here the easiest way using Vs2017 and Vs2019
Open your xsd with Visual Studio and generate a sample xml file as in the url suggested.
Once you opened your xsd in design view as below, click on xml schema explorer
2.
Within “XML Schema Explorer” scroll all the way down to find the root/data node. Right click on root/data node and it will show “Generate Sample XML”. If it does not show, it means you are not on the data element node but you are on any of the data definition node.
Copy your generated Xml into the clipboard
Create a new empty class in your solution and delete the class definition. Only Namespace should remain
While your mouse pointer focused inside your class, choose EDIT-> Paste Special-> Paste Xml as Classes
For a quick and lazy solution, (and not using VS at all) try these online converters:
xsd-to-xml-converter here
xmltocsharp converter here
XSD => XML => C# classes
Example XSD:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="shiporder">
<xs:complexType>
<xs:sequence>
<xs:element name="orderperson" type="xs:string"/>
<xs:element name="shipto">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="address" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="item" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="note" type="xs:string" minOccurs="0"/>
<xs:element name="quantity" type="xs:positiveInteger"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="orderid" type="xs:string" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
Converts to XML:
<?xml version="1.0" encoding="utf-8"?>
<!-- Created with Liquid Technologies Online Tools 1.0 (https://www.liquid-technologies.com) -->
<shiporder xsi:noNamespaceSchemaLocation="schema.xsd" orderid="string" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<orderperson>string</orderperson>
<shipto>
<name>string</name>
<address>string</address>
<city>string</city>
<country>string</country>
</shipto>
<item>
<title>string</title>
<note>string</note>
<quantity>3229484693</quantity>
<price>-6894.465094196054907</price>
</item>
<item>
<title>string</title>
<note>string</note>
<quantity>2181272155</quantity>
<price>-2645.585094196054907</price>
</item>
<item>
<title>string</title>
<note>string</note>
<quantity>2485046602</quantity>
<price>4023.034905803945093</price>
</item>
<item>
<title>string</title>
<note>string</note>
<quantity>1342091380</quantity>
<price>-810.825094196054907</price>
</item>
</shiporder>
Which converts to this class structure:
/*
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0
*/
using System;
using System.Xml.Serialization;
using System.Collections.Generic;
namespace Xml2CSharp
{
[XmlRoot(ElementName="shipto")]
public class Shipto {
[XmlElement(ElementName="name")]
public string Name { get; set; }
[XmlElement(ElementName="address")]
public string Address { get; set; }
[XmlElement(ElementName="city")]
public string City { get; set; }
[XmlElement(ElementName="country")]
public string Country { get; set; }
}
[XmlRoot(ElementName="item")]
public class Item {
[XmlElement(ElementName="title")]
public string Title { get; set; }
[XmlElement(ElementName="note")]
public string Note { get; set; }
[XmlElement(ElementName="quantity")]
public string Quantity { get; set; }
[XmlElement(ElementName="price")]
public string Price { get; set; }
}
[XmlRoot(ElementName="shiporder")]
public class Shiporder {
[XmlElement(ElementName="orderperson")]
public string Orderperson { get; set; }
[XmlElement(ElementName="shipto")]
public Shipto Shipto { get; set; }
[XmlElement(ElementName="item")]
public List<Item> Item { get; set; }
[XmlAttribute(AttributeName="noNamespaceSchemaLocation", Namespace="http://www.w3.org/2001/XMLSchema-instance")]
public string NoNamespaceSchemaLocation { get; set; }
[XmlAttribute(AttributeName="orderid")]
public string Orderid { get; set; }
[XmlAttribute(AttributeName="xsi", Namespace="http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
}
}
Attention! Take in account that this is just to Get-You-Started, the results obviously need refinements!
xsd.exe does not work well when you have circular references (ie a type can own an element of its own type directly or indirectly).
When circular references exist, I use Xsd2Code. Xsd2Code handles circular references well and works within the VS IDE, which is a big plus. It also has a lot of features you can use like generating the serialization/deserialization code. Make sure you turn on the GenerateXMLAttributes if you are generating serialization though (otherwise you'll get exceptions for ordering if not defined on all elements).
Neither works well with the choice feature. you'll end up with lists/collections of object instead of the type you want. I'd recommend avoiding choice in your xsd if possible as this does not serialize/deserialize well into a strongly typed class. If you don't care about this, though, then it's not a problem.
The any feature in xsd2code deserializes as System.Xml.XmlElement which I find really convenient but may be an issue if you want strong typed objects. I often use any when allowing custom config data, so an XmlElement is convenient to pass to another XML deserializer that is custom defined elsewhere.
I use XSD in a batch script to generate .xsd file and classes from XML directly :
set XmlFilename=Your__Xml__Here
set WorkingFolder=Your__Xml__Path_Here
set XmlExtension=.xml
set XsdExtension=.xsd
set XSD="C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1\Tools\xsd.exe"
set XmlFilePath=%WorkingFolder%%XmlFilename%%XmlExtension%
set XsdFilePath=%WorkingFolder%%XmlFilename%%XsdExtension%
%XSD% %XmlFilePath% /out:%WorkingFolder%
%XSD% %XsdFilePath% /c /out:%WorkingFolder%
The command that worked in my case was:
xsd /c your.xsd
If you want to generate the class with auto properties, convert the XSD to XML using this then convert the XML to JSON using this and copy to clipboard the result.
Then in VS, inside the file where your class will be created, go to Edit>Paste Special>Paste JSON as classes.
I used xsd.exe in the Windows command prompt.
However, since my xml referenced several online xml's (in my case http://www.w3.org/1999/xlink.xsd which references http://www.w3.org/2001/xml.xsd) I had to also download those schematics, put them in the same directory as my xsd, and then list those files in the command:
"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\xsd.exe" /classes /language:CS your.xsd xlink.xsd xml.xsd
Marc Gravells answer was right for me but my xsd was with extension of .xml. When I used xsd program it gave :
- The table (Amt) cannot be the child table to itself in nested relations.
As per this KB325695 I renamed extension from .xml to .xsd and it worked fine.
Along with WSDL, I had xsd files. The above did not work in my case gave error. It worked as follows
wsdl /l:C# /out:D:\FileName.cs D:\NameApi\wsdl_1_1\RESAdapterService.wsdl
D:\CXTypes.xsd D:\CTypes.xsd
D:\Preferences.xsd

Categories

Resources