Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / C#

Xml Schemas From Code / XML validation against Schema File And More

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
25 Oct 2015CPOL2 min read 14.6K   8  
I don’t know about you lot but I work with XML files a bit, but I don’t have to mess around with XSD (xml schema) files that often. And it seems like every time I do I forget what I did last time. To this end I thought I would write this up somewhere, so […]

I don’t know about you lot but I work with XML files a bit, but I don’t have to mess around with XSD (xml schema) files that often. And it seems like every time I do I forget what I did last time. To this end I thought I would write this up somewhere, so I can refer back to it.

So what we will we cover here?

I will be covering these things:

  1. Create a XML file from C# objects
  2. Create a XSD from a XML file using C#
  3. Validate a XML file against a XSD schema file using C#

1. Create a XML file from C# objects

So lets say we have created the following objects in C# that we wish to serialize to XML.

C#
public class OrderList
{
    public OrderList()
    {
        Orders = new List();
    }
 
    public List Orders { get; set; }
}
 
public class Order
{
 
    public OrderSummary OrderSummary { get; set; }
    public Customer Customer { get; set; }
}
 
 
public class Address
{
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string AddressLine3 { get; set; }
    public string City { get; set; }
    public string County { get; set; }
    public string PostCode { get; set; }
}
 
public class Customer
{
    public string Title { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
 
}
 
 
public class OrderSummary
{
    public OrderSummary()
    {
        OrderLines = new List();
    }
 
    public List OrderLines { get; set; }
    public Address DeliveryAddress { get; set; }
    public DateTime DeliveryDate { get; set; }
 
}
 
public class OrderLine
{
    public decimal ItemQuanity { get; set; }
    public string ItemName { get; set; }
}

How do we then take that and save it to XML? Turns out this is very easy we can just use some code like this:

C#
public static void CreateXmlFile(string filename)
{
    Address add = new Address()
    {
        AddressLine1 = "AddressLine1",
        AddressLine2 = "AddressLine2",
        AddressLine3 = "AddressLine3",
        City = "City",
        County = "County",
        PostCode = "PostCode"
    };
 
    Customer cust = new Customer()
    {
        Email = "Email",
        FirstName = "John",
        LastName = "Barnes",
        Phone = "13311",
        Title = "Mr"
    };
 
 
    OrderList orders = new OrderList();
 
 
    var orderSummary = new OrderSummary()
    {
        DeliveryAddress = add,
        DeliveryDate = DateTime.Now,
        OrderLines = new List()
        {
            new OrderLine() {ItemQuanity = 150, ItemName = "TestItem1" },
            new OrderLine() {ItemQuanity = 250, ItemName = "TestItem2" },
            new OrderLine() {ItemQuanity = 4, ItemName = "TestItem3" },
        },
    };
 
 
    //order1
    Order order1 = new Order();
    order1.Customer = cust;
    order1.OrderSummary = orderSummary;
    orders.Orders.Add(order1);
 
 
    //order2
    Order order2 = new Order();
    order2.Customer = cust;
    order2.OrderSummary = orderSummary;
    orders.Orders.Add(order1);
 
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(OrderList));
 
    using (FileStream stream = File.OpenWrite(filename))
    {
        xmlSerializer.Serialize(stream, orders);
    }
}

That is enough to write a XML file to disk that matches your C# objects. Cool so far. Let’s continue

2. Create a XSD from a XML file using C#

Now there are many ways to do this. Here are some choices

  • Double click a valid XML in Visual Studio and use the Xml menu to create a schema
  • Use the xsd.exe command line tool which you would use something like xsd.exe SomeXmlFile.xml
  • Use C# to programmatically write out a XSD file that matches some object definition

In the past I would have use the XSD.exe command line to do this. But a strange this happens when I take the output of that and try and include it in Visual Studio. Visual Studio tries to create a strongly typed DataSet out of the XSD file. I don’t want this, I want it to remain as a XSD schema file. I think there is a way to stop this happening by altering the file produced by xsd.exe, but there are 2 other ways. For this I have chosen to use a programmatical approach, which is as follows:

C#
public static void CreateSchemaFromXml(string fileName)
{
 
    //CREATE SCHEMA FROM XML
 
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(OrderList));
 
    XmlSchemas schemas = new XmlSchemas();
    XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
 
    XmlTypeMapping mapping = new XmlReflectionImporter()
        .ImportTypeMapping(typeof(OrderList));
    exporter.ExportTypeMapping(mapping);
    var schemasData = TrimSchema(schemas);
 
    using (FileStream stream = File.OpenWrite(fileName))
    {
        schemasData.First().Write(stream);
    }
}
 
private static List TrimSchema(XmlSchemas schemas)
{
    List schemasData = new List(
        schemas.Where(s => s.TargetNamespace != "http://www.w3.org/2001/XMLSchema" &&
        s.TargetNamespace != "http://microsoft.com/wsdl/types/"));
 
    return schemasData;
}

This will produce a valid XSD file on disk that you may then fiddle with by adding more restrictions say.  So now all we need to do is carry out some validation of XML file(s) against this XSD file.

3. Validate a XML file against a XSD schema file using C#

This is easily achieved using some test code. But before we look at that, this is what the project looks like in Visual Studio

image

So you can see that there is a folder with good and bad files that I wish to test against the XSD file. Here is the complete test case code:

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.Schema;
using NUnit.Framework;
 
namespace XmlTests
{
    [TestFixture]
    public class StaticXmlFileTests
    {
 
         
        //Baddies
        [TestCase(@"\Xml\BadAgainstSchema\OrdersBADExampleFileNoOrderSummary.xml", false)]
        [TestCase(@"\Xml\BadAgainstSchema\OrdersBADExampleFile_AddressLineTooLong.xml", false)]
 
 
        //Goodies
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_FullFeatureSet.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_MultipleOrderLines.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_MultipleOrders.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_SingleOrder.xml", true)]
        [TestCase(@"\Xml\Good\OrdersGOODExampleFile_SingleOrderLine.xml", true)]
        public void TestFileProducesExpectedSchemaValidationResult(string filename, bool exepectedValidationResult)
        {
 
 
            var xmlFile = ObtainFullFilePath(filename);
            var xsdFile = ObtainFullFilePath(@"\Xml\OrdersExampleFile.xsd");
 
            //VALIDATE XML AGAINST SCHEMA C#
            var xdoc = XDocument.Load(xmlFile);
            var schemas = new XmlSchemaSet();
            using (FileStream stream = File.OpenRead(xsdFile))
            {
                schemas.Add(XmlSchema.Read(stream, (s, e) =>
                {
                    var x = e.Message;
                }));
            }
 
            bool isvalid = true;
            StringBuilder sb = new StringBuilder();
            try
            {
                xdoc.Validate(schemas, (s, e) => 
                    {
                        isvalid = false;
                        sb.AppendLine(string.Format("Line : {0}, Message : {1} ", 
                            e.Exception.LineNumber, e.Exception.Message));
                    });
            }
            catch (XmlSchemaValidationException)
            {
                isvalid = false;
            }
 
            var validationErrors = sb.ToString();
            Assert.AreEqual(exepectedValidationResult, isvalid);
            if (exepectedValidationResult)
            {
                Assert.AreEqual(string.Empty, validationErrors);
            }
            else
            {
                Assert.AreNotEqual(string.Empty, validationErrors);
            }
 
        }
 
 
 
        private string ObtainFullFilePath(string fileName)
        {
            var path = TestContext.CurrentContext.TestDirectory;
            return string.Format("{0}{1}", path, fileName);
        }
    }
}

And here is a screen shot of all the tests working as expected:

image

You can find a small project for this on github:  https://github.com/sachabarber/XmlTests

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
-- There are no messages in this forum --