I have static method, which i use to validate a XML File against a XSD File. This works fine, until there is an XSD File which includes another XSD File.
Example, where i got troubles:
TYPES.XSD:
<xs:simpleType name="MY_AMOUNT">
<xs:restriction base="xs:decimal">
<xs:maxInclusive value="999999999999.99"/>
<xs:minInclusive value="-999999999999.99"/>
<xs:totalDigits value="14"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
MAIN.XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="TYPES.xsd"/>
<xs:element name="ROOT">
<xs:complexType>
<xs:sequence>
<xs:element ref="SOMEREF1"/>
<xs:element ref="SOMEREF2"/>
<xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
VALIDATION CODE:
public static class XmlUtils
{
private static string Errors = string.Empty;
public static bool ValidateAgainstXSD(string xmlFilePath, string xsdFilePath, ref string message)
{
try
{
var settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Schema;
settings.ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema
| XmlSchemaValidationFlags.ProcessInlineSchema
| XmlSchemaValidationFlags.ReportValidationWarnings;
settings.Schemas.Add(null, xsdFilePath);
settings.Schemas.Compile();
settings.ValidationEventHandler += (sender, args) =>
{
if (args.Severity == XmlSeverityType.Error)
{
Errors += args.Message + "\n";
}
};
using (var reader = XmlReader.Create(xmlFilePath, settings))
{
while (reader.Read()) { }
}
message = Errors ?? string.Empty;
return string.IsNullOrEmpty(Errors);
}
catch (Exception e)
{
message = "# error validating xml file: " + e.Message;
return false;
}
}
}
Somehow it seems i have to specify the path of the included XSD File but i have no idea where.
The error occurs at settings.Schemas.Compile(); , where it says that the type "MY_AMOUNT" is not declared. I read about custom XmlResolvers but to be honest i didn't get that working.
If this is important for an answer: The xsd files are always located in the same directory!
The method is called likes this:
string msg = string.Empty;
string basedir = #"C:\Temp";
string xml = Path.Combine(basedir, "XML_FILE.xml");
string xsd = Path.Combine(basedir, "MAIN.xsd");
if (XmlUtils.ValidateAgainstXSD(xml, xsd, ref msg))
{
// do some work
}
else
{
Console.WriteLine(msg);
}
Console.ReadLine();
Any help is highly appreciated - Thank you!
UPDATE 2016-12-05:
I wrote my own XmlUrlResolver, to see what happens behind the scenes:
internal class XUrlResolver : XmlUrlResolver
{
public override object GetEntity(Uri absoluteUri, string role, Type ofObjectToReturn)
{
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
public override Uri ResolveUri(Uri baseUri, string relativeUri)
{
return base.ResolveUri(baseUri, relativeUri);
}
}
And the i just try to do:
XmlSchemaSet xset = new XmlSchemaSet();
xset.XmlResolver = new XUrlResolver();
xset.Add("", xsdFilePath);
xset.Compile();
What happens now (on line xset.Add):
XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD") --> {file:///C:/Temp/MAIN.xsd}
XmlUrlResolver.ResolveUri(null,"C:\\Temp\\MAIN.XSD") --> {file:///C:/Temp/MAIN.xsd}
XmlUrlResolver.GetEntity({file:///C:/Temp/MAIN.xsd}) --> Filestream to MAIN.xsd
XmlUrlResolver.ResolveUri({file:///C:/Temp/MAIN.xsd},"TYPES.XSD") --> {file:///C:/Temp/TYPES.xsd}
XmlUrlResolver.GetEntity({file:///C:/Temp/TYPES.xsd}) --> Filestream to TYPES.xsd
Looks good to me (except the first 2 Calls are equal!?!) - the path to TYPES.XSD is resolved as it should.
Nevertheless, xset.Compile() throws an Exception: "Type MY_AMOUNT is not declared"
And i have no idea why :/
First you need to make your xsd files valid.
Types.xsd (added schema root element and xs namespace)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="MY_AMOUNT">
<xs:restriction base="xs:decimal">
<xs:maxInclusive value="999999999999.99"/>
<xs:minInclusive value="-999999999999.99"/>
<xs:totalDigits value="14"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Main.xsd (removed invalid refs).
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="TYPES.xsd"/>
<xs:element name="ROOT">
<xs:complexType>
<xs:sequence>
<xs:element name="AMOUNT" type="MY_AMOUNT" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
After that, given that both xsd files are in the same directory, your schemas will compile fine.
I ran into this very same issue.
I'm not suggesting this is the correct answer, but I got around it by setting the Environment.CurrentDirectory property to be the path where the included XSDs were located. Then it all processed just fine.
Related
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 (e.g. 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:
By me defining Rules to how the xsd should look like I take away
flexibility that I would like to have.
This approach seems quite errorprone to me because it seems like basically a little better than direct string manipulation.
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; }
}
}
I have an xsd file that describes a small schema. I created a C# file with the help of xsd.exe (I ran this command in the developer command prompt: xsd.exe smallSchema.xsd /classes /language:CS) to be able to easily (de-)serialize it. I took an xml file that complies to that schema and tried to deserialize it by using the generated code. But i noticed that data loss occurs!
Can anyone point me to the cause of that data loss?
This is the xml file that I want to deserialize:
<?xml version="1.0" encoding="UTF-8"?>
<top-element>
<complex-elem type="plain-number">1</complex-elem>
<top-elem-name>myTopElement</top-elem-name>
</top-element>
The following code will deserialize it, then serialize it again and overwrite the file:
XmlSerializer serializer = new XmlSerializer(typeof(topelement));
string path = ... // path of the file on my disk
topelement rawData = null;
using (FileStream reader = new FileStream(path, FileMode.Open))
{
rawData = (topelement)serializer.Deserialize(reader);
}
XmlSerializerNamespaces noNamespace
= new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName("", "") });
// I use it to prevent adding a namespace during serializing
using (XmlWriter wr = XmlWriter.Create(path))
{
serializer.Serialize(wr, rawData, noNamespace);
}
But the overwritten file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<top-element>
<complex-elem />
<top-elem-name>myTopElement</top-elem-name>
</top-element>
I.e. the data inside complex-elem got lost! Debugging shows that the deserialized content already contains null where it shouldn't (see picture).
This is the xsd schema:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="top-element">
<xs:complexType>
<xs:sequence>
<xs:element ref="complex-elem" />
<xs:element ref="top-elem-name" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="complex-elem">
<xs:complexType>
<xs:choice>
<xs:element ref="plain-number" />
</xs:choice>
</xs:complexType>
</xs:element>
<xs:element name="plain-number" type="xs:string" />
<xs:element name="top-elem-name" type="xs:string" />
</xs:schema>
And here is the result of calling xsd.exe (the comments are German):
//------------------------------------------------------------------------------
// <auto-generated>
// Dieser Code wurde von einem Tool generiert.
// Laufzeitversion:4.0.30319.42000
//
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
// der Code erneut generiert wird.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// Dieser Quellcode wurde automatisch generiert von xsd, Version=4.6.1055.0.
//
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute("top-element", Namespace="", IsNullable=false)]
public partial class topelement {
private complexelem complexelemField;
private string topelemnameField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("complex-elem")]
public complexelem complexelem {
get {
return this.complexelemField;
}
set {
this.complexelemField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("top-elem-name")]
public string topelemname {
get {
return this.topelemnameField;
}
set {
this.topelemnameField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.6.1055.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)]
[System.Xml.Serialization.XmlRootAttribute("complex-elem", Namespace="", IsNullable=false)]
public partial class complexelem {
private string itemField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("plain-number")]
public string Item {
get {
return this.itemField;
}
set {
this.itemField = value;
}
}
}
The XML sample you try to deserialize doesn't validate against the XSD schema you provided.
If you try to validate the XML, you'll get three validation errors:
Cvc-complex-type.3.2.2: Attribute 'type' Is Not Allowed To Appear In Element 'complex-elem'., Line '2', Column '39'.
Cvc-complex-type.2.3: Element 'complex-elem' Cannot Have Character [children], Because The Type's Content Type Is Element-only., Line
'2', Column '55'.
Cvc-complex-type.2.4.b: The Content Of Element 'complex-elem' Is Not Complete. One Of '{plain-number}' Is Expected., Line '2', Column
'55'.
So either change your XML to the following one:
<?xml version="1.0" encoding="UTF-8"?>
<top-element>
<complex-elem>
<plain-number>1</plain-number>
</complex-elem>
<top-elem-name>myTopElement</top-elem-name>
</top-element>
... or change your complex-elem schema definition to this one:
<xs:element name="complex-elem">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="type" type="xs:string" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
I have the following xsd snippet:
<xs:element name="TR" type="tns:blah" />
<xs:complexType name="blah">
<xs:sequence>
<xs:element minOccurs="1" maxOccurs="1" name="Res" type="tns:Res" />
<xs:element minOccurs="0" maxOccurs="1" name="SNotifications" type="tns:ArrayOfSNotification" />
<xs:element minOccurs="0" maxOccurs="1" name="UNotifications" type="tns:ArrayOfUNotification" />
<xs:element minOccurs="0" maxOccurs="1" name="TNotifications" type="tns:ArrayOfTNotification" />
</xs:sequence>
</xs:complexType>
and I have the following xml:
<TR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://something.com/something">
<Res>
<CC>CMG</CC>
<CT>2014-07-24T14:10:03.84</CT>
<BN>994807</BN>
</Res>
<SNotifications xmlns="http://something.com/something" />
<UNotifications xmlns="http://something.com/something" />
<TNotifications xmlns="http://something.com/something" />
</TR>
I get no error validation the xml in notepad++.
but i get an error while using C#.
The error is:
exception:System.Xml.Schema.XmlSchemaValidationException: The element 'TR' in namespace 'http://something.com/something' has invalid child element 'SNotifications'. List of possible elements expected: 'SNotifications, TNotifications, UNotifications'
the C# code i use is:
xmlDocument.Schemas.Add("http://www.something.com/something", "path to xsd file");
string result = string.Empty;
xmlDocument.Validate((s, e) => result = string.Format("exception:{0}, exceptionmessage:{1}", e.Exception, e.Message));
I have already loaded the xml document.
Any help would be very much appreciated.
Thank you in advance,
gmat
Please try this.
public static bool IsValidXml(string xmlFilePath, string xsdFilePath)
{
var xdoc = XDocument.Load(xmlFilePath);
var schemas = new XmlSchemaSet();
schemas.Add(namespaceName, xsdFilePath);
Boolean result = true;
xdoc.Validate(schemas, (sender, e) =>
{
result = false;
});
return result;
}
We need to know the targetNamespace of your schema document, and the setting of elementFormDefault. Almost certainly it's one of the following:
(a) you have elementFormDefault="qualified", and the target namespace of the schema is not http://something.com/something (in which case the SNotifications element should be in the target namespace); or
(b) you don't have elementFormDefault="qualified" (in which case SNotifications should be in no namespace).
I have a very valid XMl string in memory downloaded from an OGC complaint web feature service.
When I use the following code to create am XmlTextReader to parse to my parser,
using (var sr = new StringReader(schemaString))
{
using (var reader = new XmlTextReader(sr))
{
try
{
schema = new GML2Parser().GetClassDefinition(reader, schema);
}
catch (Exception ex)
{
error = ex.Message;
}
}
}
I get an exception indicating Data at rool level is invalid. If I save this string to a local file say feature_desc.xsd the use File.ReadAllText and call the aforementioned routine, I run into a similar problem.
However, if I use XmlReader.Create(feature_desc.xsd), my parser does not throw an exception when it starts traversing the XML nodes. This is a method that summarizes these actions;
private void ParseFeatureDescription(DataTableInfo schema, string featureDescription, string featureFileName, string featureName)
{
var schemaLocation = string.Empty;
if (featureFileName != null)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(featureDescription);
schemaLocation = infrut.Utilities.CreateTempFilePath(featureFileName, FileExtension.xsd, false);
doc.Save(schemaLocation);
}
var error = DeserializeTableSchema(schema, featureDescription, featureName);
if (!string.IsNullOrEmpty(error))
{
var fromFileFeatureDesc = File.ReadAllText(schemaLocation);
if (featureDescription == fromFileFeatureDesc){}
error = DeserializeTableSchema(schema, fromFileFeatureDesc, featureName);
if (!string.IsNullOrEmpty(error))
{
// last resort
var reader = XmlReader.Create(schemaLocation);
schema = new GML2Parser().GetClassDefinition(reader, schema);
if (schema.Columns.Count == 0)
{
// trouble
ActionResponse.LogError("Error parsing description of " + featureName + ". Inner exception is \r\n" + error
+ " " + " for content \r\n" + fromFileFeatureDesc, "WFS Worker");
}
}
}
}
In memory representation of the string is:
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:ems=\"http://www.emssatcom.com\" xmlns:gml=\"http://www.opengis.net/gml\" elementFormDefault=\"qualified\" targetNamespace=\"http://www.emssatcom.com\">\r\n <xsd:import namespace=\"http://www.opengis.net/gml\" schemaLocation=\"http://10.25.131.62:8091/geoserver/schemas/gml/2.1.2/feature.xsd\" />\r\n <xsd:complexType name=\"asmcc_srr_viewType\">\r\n <xsd:complexContent>\r\n <xsd:extension base=\"gml:AbstractFeatureType\">\r\n <xsd:sequence>\r\n <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"gid\" nillable=\"true\" type=\"xsd:int\" />\r\n <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"srr_name\" nillable=\"true\" type=\"xsd:string\" />\r\n <xsd:element maxOccurs=\"1\" minOccurs=\"0\" name=\"the_geom\" nillable=\"true\" type=\"gml:PolygonPropertyType\" />\r\n </xsd:sequence>\r\n </xsd:extension>\r\n </xsd:complexContent>\r\n </xsd:complexType>\r\n <xsd:element name=\"asmcc_srr_view\" substitutionGroup=\"gml:_Feature\" type=\"ems:asmcc_srr_viewType\" />\r\n</xsd:schema>"
and persisted file is:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ems="http://www.emssatcom.com" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" targetNamespace="http://www.emssatcom.com">
<xsd:import namespace="http://www.opengis.net/gml" schemaLocation="http://10.25.131.62:8091/geoserver/schemas/gml/2.1.2/feature.xsd" />
<xsd:complexType name="asmcc_srr_viewType">
<xsd:complexContent>
<xsd:extension base="gml:AbstractFeatureType">
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="0" name="gid" nillable="true" type="xsd:int" />
<xsd:element maxOccurs="1" minOccurs="0" name="srr_name" nillable="true" type="xsd:string" />
<xsd:element maxOccurs="1" minOccurs="0" name="the_geom" nillable="true" type="gml:PolygonPropertyType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:element name="asmcc_srr_view" substitutionGroup="gml:_Feature" type="ems:asmcc_srr_viewType" />
</xsd:schema>
Any one run into this?
Wild guess here: sometimes I get this error (line 1 col 1) in different applications because they stored in UTF-8 encoding and they have byte order mark at the very beginning of the text/file.
http://en.wikipedia.org/wiki/Byte_order_mark
Try to read file as ANSI string, not unicode
I have problems getting the Attributes of a XmlSchema sub element.
There is an abstactElement and a concreteElement which extends the abstractElement.
Getting the base's attributes works fine using XmlSchemaComplexType.BaseXmlSchemaType.
But getting the concreteElement's attributes using XmlSchemaComplexType.Attributes does not work.
This is my example Xml-Schema file:
<xs:schema id="XMLSchema1"
targetNamespace="http://tempuri.org/XMLSchema1.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/XMLSchema1.xsd"
xmlns:mstns="http://tempuri.org/XMLSchema1.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xs:element name = "ConcreteElement" type="concreteElement" />
<xs:complexType name="abstractElement">
<xs:attribute name="aA1" type="xs:string" />
<xs:attribute name="aA2" type="xs:string" />
<xs:attribute name="aA3" type="xs:string" />
</xs:complexType>
<xs:complexType name="concreteElement">
<xs:complexContent>
<xs:extension base="abstractElement">
<xs:attribute name="cA1" type="xs:string"/>
<xs:attribute name="cA2" type="xs:string"/>
<xs:attribute name="cA3" type="xs:string"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
I want to get all attributes of the ConcreteElement (cA1, cA2, cA3) and all the attributes of its base element (aA1, aA2, aA3).
My code looks like that:
public Program()
{
XmlSchema xsd =
XmlSchema.Read(
new StreamReader("/Path/To/My/Xsd/File.xsd"),
null);
var xss = new XmlSchemaSet();
xss.Add(xsd);
xss.Compile();
XmlSchemaElement xsdRoot = null;
/* Get the root element */
foreach (DictionaryEntry curEle in xsd.Elements)
{
var xse = (XmlSchemaElement)curEle.Value;
xsdRoot = xse;
break;
}
List<XmlSchemaAttribute> lsAttributes = this.GetAllAttributes(
xsdRoot.ElementSchemaType as XmlSchemaComplexType);
foreach (XmlSchemaAttribute curAtr in lsAttributes)
{
Console.WriteLine(curAtr.Name);
}
Console.ReadKey();
}
And this is my GetAllAttributes method:
private List<XmlSchemaAttribute> GetAllAttributes(
XmlSchemaComplexType comCon)
{
/* No Ancestor, no Attributes */
if (comCon == null)
{
return new List<XmlSchemaAttribute>();
}
/* Get attributs of the acestors */
List<XmlSchemaAttribute> allAttributes =
this.GetAllAttributes(
comCon.BaseXmlSchemaType as XmlSchemaComplexType);
/* Ad the attributes of the given element */
allAttributes.AddRange(comCon.Attributes.Cast<XmlSchemaAttribute>());
return allAttributes;
}
Regards,
Finally the solution is to use the Property AttributeUses, which holds all the attributes
of an element, even those which belong to the ancestors.
private List<XmlSchemaAttribute> GetAllAttributes(
XmlSchemaComplexType comCon)
{
List<XmlSchemaAttribute> allAttributes = new List<XmlSchemaAttribute>();
/* Add the attributes of the given element */
foreach (DictionaryEntry curAttriEntry in comCon.AttributeUses)
{
XmlSchemaAttribute curAttri =
curAttriEntry.Value as XmlSchemaAttribute;
if (curAttri != null)
{
allAttributes.Add(curAttri);
}
}
return allAttributes;
}