Search code examples
c#xmldeserializationxmlserializer

C# Deserialization failure on enabling schema validation


I serialized a class to XML. But deserialization to the same class type is failing when schema validation is enabled.

Here is what I'm doing:

  1. creating an object from the serializable class
  2. Serializing that object to XML
  3. Gets the schema from the that object
  4. Adds that schema to validation
  5. deserialize with out validation
  6. deserialize with XMLschema validation

In step six, it is failing...

Here in this code sample, method with validation is failing:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Deserialize
{
    public class Program
    {
        static string filepath = "TestSerilize.xml";
        private static object oSchema;
        private static XmlReaderSettings oXmlReaderSettings;

        static void Main(string[] args)
        {
            MyObject oMyobject = new MyObject();
            oMyobject.MyObjectType = "MyCustomType";
            List<Items> olistItems = new List<Items>();
            Items oItems = new Items();
            oItems.key = "test123";
            oItems.value = "testvalue";
            olistItems.Add(oItems);
            oMyobject.Items = olistItems;
            Saveobjecttofile(oMyobject, filepath);
            dynamic objDeserialized = null;
            objDeserialized = GetObjFormfileWithoutValidation(filepath, oMyobject.GetType());
            objDeserialized = GetObjFormfileWithValidation(filepath, oMyobject.GetType());

        }

        private static dynamic GetObjFormfileWithValidation(string filepath, Type type)
        {
            XmlReaderSettings oXmlReaderSettings = new XmlReaderSettings();
            oXmlReaderSettings.ValidationType = ValidationType.Schema;
            dynamic oSchema = GetSchemaFromType(type);
            oXmlReaderSettings.Schemas.Add(oSchema);
            XmlReader oXmlReader = null;
            if (oSchema != null)
            {
                oXmlReader = XmlReader.Create(filepath, oXmlReaderSettings);
            }
            else
            {
                oXmlReader = XmlReader.Create(filepath);
            }
            object obj = null;
            try
            {
                XmlSerializer oXmlSerializer = new XmlSerializer(type);
                obj = oXmlSerializer.Deserialize(oXmlReader);
            }
            finally
            {
                oXmlReader.Close();
            }
            return obj;
        }

        private static XmlSchema GetSchemaFromType(Type type)
        {
            var oSoapReflectionImporter = new SoapReflectionImporter();
            var oXmlTypeMapping = oSoapReflectionImporter.ImportTypeMapping(type);
            var oXmlSchemas = new XmlSchemas();
            var oXmlSchema = new XmlSchema();
            oXmlSchemas.Add(oXmlSchema);
            var oXMLSchemaExporter = new XmlSchemaExporter(oXmlSchemas);
            oXMLSchemaExporter.ExportTypeMapping(oXmlTypeMapping);
            return oXmlSchema;
        }

        private static dynamic GetObjFormfileWithoutValidation(string filepath, Type type)
        {
            XmlReader oXmlReader = null;
            oXmlReader = XmlReader.Create(filepath);
            object obj = null;
            try
            {
                XmlSerializer oXmlSerializer = new XmlSerializer(type);
                obj = oXmlSerializer.Deserialize(oXmlReader);
            }
            finally
            {
                oXmlReader.Close();
            }
            return obj;

        }

        private static void Saveobjecttofile(object objectToSave, string filepath)
        {
            try
            {
                System.Xml.Serialization.XmlSerializer oXmlSerializer = new System.Xml.Serialization.XmlSerializer(objectToSave.GetType());
                using (System.Xml.XmlTextWriter oXmlTextWriter = new System.Xml.XmlTextWriter(filepath, System.Text.Encoding.UTF8))
                {
                    oXmlTextWriter.Indentation = 2;
                    oXmlTextWriter.Formatting = System.Xml.Formatting.Indented;
                    oXmlSerializer.Serialize(oXmlTextWriter, objectToSave);
                    oXmlTextWriter.Flush();
                    oXmlTextWriter.Close();
                }
            }
            catch (Exception)
            { throw; }
        }
    }
    [XmlType("Items")]
    public class Items
    {
        [XmlAttribute("key")]
        public string key { get; set; }
        [XmlText()]
        public string value { get; set; }
    }

    [Serializable, XmlRoot("MyObject")]
    public class MyObject
    {
        [XmlElement("MyObjectType", IsNullable = true)]
        public string MyObjectType { get; set; }
        [XmlElement("Items")]
        public List<Items> Items;
        public string this[string key]
        {
            get
            {
                return null != Items.Find(x => x.key == key) ? Items.Find(x => x.key == key).value : null;
            }
            set
            {
                if (Items == null) Items = new List<Items>();
                if (null != Items.Find(x => x.key == key))
                {
                    Items.Find(x => x.key == key).value = value;
                }
                else
                {
                    Items.Add(new Items { key = key, value = value });
                }
            }
        }
    }
}

Exception details:

System.Xml.Schema.XmlSchemaException
Message:There is an error in XML document (3, 10).
Inner Exception message:The 'key' attribute is not declared.
StackTrace:    
at system.Xml.Schema.XmlSchemaValidator.SendValidationEvent(XmlSchemaValidationException e, XmlSeverityType severity)
at System.Xml.Schema.XmlSchemaValidator.SendValidationEvent(String code, String arg)
at System.Xml.Schema.XmlSchemaValidator.ValidateAttribute(String lName, String ns, XmlValueGetter attributeValueGetter, String attributeStringValue, XmlSchemaInfo schemaInfo)
at System.Xml.Schema.XmlSchemaValidator.ValidateAttribute(String localName, String namespaceUri, XmlValueGetter attributeValue, XmlSchemaInfo schemaInfo)
at System.Xml.XsdValidatingReader.ValidateAttributes()
at System.Xml.XsdValidatingReader.ProcessElementEvent()
at System.Xml.XsdValidatingReader.ProcessReaderEvent()
at System.Xml.XsdValidatingReader.Read()
at System.Xml.XmlReader.MoveToContent()
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMyObject.Read3_MyObject(Boolean isNullable, Boolean checkType)
at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMyObject.Read4_MyObject()

Demo fiddle here.


Solution

  • Your problem is here:

    private static XmlSchema GetSchemaFromType(Type type)
    {
        var oSoapReflectionImporter = new SoapReflectionImporter();
    

    SoapReflectionImporter is designed to generate a schema for a c# type that has been marked with SOAP attributes. Such a schema can be used to generate an XmlSerializer customized to use such attributes, as shown in How to: Serialize an Object as a SOAP-Encoded XML Stream:

    XmlTypeMapping myTypeMapping = new SoapReflectionImporter().ImportTypeMapping(type);
    XmlSerializer mySerializer = new XmlSerializer(myTypeMapping);  
    

    You, however, are not using SOAP attributes. You are using regular XmlSerializer attributes, as can be see e.g. in your Items class:

    [XmlType("Items")]
    public class Items
    {
        [XmlAttribute("key")]
        public string key { get; set; }
        [XmlText()]
        public string value { get; set; }
    }
    

    Thus you should be using XmlReflectionImporter instead:

    private static XmlSchema GetSchemaFromType(Type type)
    {
        var oReflectionImporter = new XmlReflectionImporter();
        var oXmlTypeMapping = oReflectionImporter.ImportTypeMapping(type);
        var oXmlSchemas = new XmlSchemas();
        var oXmlSchema = new XmlSchema();
        oXmlSchemas.Add(oXmlSchema);
        var oXMLSchemaExporter = new XmlSchemaExporter(oXmlSchemas);
        oXMLSchemaExporter.ExportTypeMapping(oXmlTypeMapping);
        return oXmlSchema;
    }
    

    Related: How do I programmatically generate an xml schema from a type?

    Fixed sample demo here.