简体   繁体   中英

Create xsd schema from instance of class at runtime

I need to validate some generic sensor input. The requirement is, that the validation cannot happen in my code but with a external validator like xsd from outside the codebase to give users the ability to swap the validation logic without needing to code or recompile the application. I know that the sensor input is only valid for one specific case and therefore would like to generate the xsd from an Instance of a class, that exists at runtime, that was user validated, to get the valid restrictions.

I tried the Idea from this question, however this only works on types and not on instances of classes.

Therefore my question: Is there a way to take a runtime instance of a C# class and convert it to an xsd that has the values of the properties as the only valid restrictions?

Update:

to clarify: What I have is a class like this:

public sealed class Sensor
    {
        public int Data { get; set; }

        public int otherData { get; set; }

        public int MoreData { get; set; }
    }

the class gets instanciated somewhere (eg like this):

var se = new Sensor()
            {
                Data = 5,
                otherData = 10,
                MoreData = 15
            };

When I now try to create an xsd using something like the following function:

var schemas = new XmlSchemas();
var exporter = new XmlSchemaExporter(schemas);
var mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(Person));
exporter.ExportTypeMapping(mapping);
var schemaWriter = new StringWriter();
foreach (XmlSchema schema in schemas)
{
    schema.Write(schemaWriter);
}
return schemaWriter.ToString();

I receive some xsd like this:

<?xml version="1.0" encoding="utf-8"?>
    <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
      <xs:element name="sensor">
        <xs:complexType>
          <xs:sequence>
            <xs:element name="Data" type="xs:integer" />
            <xs:element name="otherData" type="xs:integer" />
            <xs:element name="moreData" type="xs:integer" />
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:schema>

However this is far from what I want to archieve. I would like to have the proper restrictions built into it (it should look something like this):

<?xml version="1.0" encoding="utf-8"?>
        <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
          <xs:element name="sensor">
            <xs:complexType>
              <xs:sequence>
                <xs:element name="Data">
                    <xs:simpleType>
                        <xs:restriction base="xs:integer">
                            <xs:enumeration value="5"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
                <xs:element name="otherData">
                    <xs:simpleType>
                        <xs:restriction base="xs:integer">
                            <xs:enumeration value="10"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
                <xs:element name="moreData">
                    <xs:simpleType>
                        <xs:restriction base="xs:integer">
                            <xs:enumeration value="15"/>
                        </xs:restriction>
                    </xs:simpleType>
                </xs:element>
              </xs:sequence>
            </xs:complexType>
          </xs:element>
        </xs:schema>

I could obviously go ahead load the generated file into memory, strip some attributes and change how the xsd should look like, but this feels wrong because of the following things:

  1. By me defining Rules to how the xsd should look like I take away flexibility that I would like to have.
  2. This approach seems quite errorprone to me because it seems like basically a little better than direct string manipulation.
  3. This extra code would make my already large code way complexer and harder to understand.

To sum up: I need either a library or a really clever function that can create a xsd like the one above based on the runitme info I have on the class without writing a lot of things to manipulate the xml directly to avoid errorprone or wrong assumptions about the future usage of the validation.

I took your generate schema and added details using Xml Linq. See code below

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.IO;

namespace ConsoleApplication131
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            Sensor se = new Sensor()
            {
                Data = 5,
                otherData = 10,
                MoreData = 15
            };

            XmlSchemas schemas = new XmlSchemas();
            XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
            XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(Sensor));
            exporter.ExportTypeMapping(mapping);
            StringWriter schemaWriter = new StringWriter();
            foreach (XmlSchema schema in schemas)
            {
                schema.Write(schemaWriter);
            }
            XDocument doc = XDocument.Parse(schemaWriter.ToString());
            XElement root = doc.Root;
            XNamespace xs = root.GetNamespaceOfPrefix("xs");

            foreach (XElement _class in doc.Descendants(xs + "complexType"))
            {
                List<XElement> elements = _class.Descendants(xs + "element").ToList();
                if (elements.Count > 0)
                {
                    XElement complexType = new XElement(xs + "complexType");
                    _class.Add(complexType);
                    XElement sequence = new XElement(xs + "sequence");
                    complexType.Add(sequence);

                    foreach (var prop in se.GetType().GetProperties())
                    {
                        string name = prop.Name;
                        string value = prop.GetValue(se, null).ToString();
                        XElement element = elements.Where(x => (string)x.Attribute("name") == name).FirstOrDefault();
                        string strType = (string)element.Attribute("type");

                        XElement newElement = new XElement(xs + "simpleType", new object[] {
                        new XElement(xs + "restriction", new object[] {
                            new XAttribute("base", strType),
                            new XElement(xs + "enumeration", new XAttribute("value", value))
                        })
                    });
                        sequence.Add(newElement);

                    }
                }
            }
            doc.Save(FILENAME);

        }
    }
    public sealed class Sensor
    {
        public int Data { get; set; }
        public int otherData { get; set; }
        public int MoreData { get; set; }
    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM