static XElement stripNS(XElement root) {
return new XElement(
root.Name.LocalName,
root.HasElements ?
root.Elements().Select(el => stripNS(el)) :
(object)root.Value
);
}
static void Main() {
var xml = XElement.Parse(@"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfInserts xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<insert>
<offer xmlns=""http://schema.peters.com/doc_353/1/Types"">0174587</offer>
<type2 xmlns=""http://schema.peters.com/doc_353/1/Types"">014717</type2>
<supplier xmlns=""http://schema.peters.com/doc_353/1/Types"">019172</supplier>
<id_frame xmlns=""http://schema.peters.com/doc_353/1/Types"" />
<type3 xmlns=""http://schema.peters.com/doc_353/1/Types"">
<type2 />
<main>false</main>
</type3>
<status xmlns=""http://schema.peters.com/doc_353/1/Types"">Some state</status>
</insert>
</ArrayOfInserts>");
Console.WriteLine(stripNS(xml));
}
I needed to validate an XML document with a given XSD document. Seems easy enough… so let’s have a look at the schema first:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://my.namespace"
elementFormDefault="qualified"
targetNamespace="http://my.namespace">
<xs:element name="customer">
<xs:complexType>
<xs:sequence>
<xs:element name="firstname" type="xs:string" />
<xs:element name="lastname" type="xs:string" />
<xs:element name="age" type="xs:integer" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
The XML instance is:
<?xml version="1.0" encoding="utf-8" ?>
<customer>
<firstname>Homer</firstname>
<lastname></lastname>
<age>36</age>
</customer>
The code is straightforward:
static void Main(string[] args)
{
// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
}
static void ValidationCallback(object sender,
System.Xml.Schema.ValidationEventArgs e)
{
Console.WriteLine(string.Format("[{0}] {1}", e.Severity, e.Message));
}
If you run this, no errors are thrown so it seems to validate. To be sure, let’s change the age in an invalid value:
<Age>invalid!</Age>
and test again. Well… actually, no validation error is thrown in this case either… what’s going on here?
Actually, the XML is not validated at all, because it’s not in the same namespace (http://my.namespace) as the schema definition. This is very dangerous, as we might easily get mislead by thinking that it validates because no errors are thrown. So how do we solve it?
We could ask the sender to provide the correct namespace in the XML file – this would be the best solution because then it would just work – if you try to validate the following XML:
<?xml version="1.0" encoding="utf-8" ?>
<customer xmlns="http://my.namespace">
<firstname>Homer</firstname>
<lastname></lastname>
<age>invalid</age>
</customer>
…then the validation error is thrown, because the namespaces now match:
Unfortunately, it is not always possible to change the XML file, so how can we bypass this namespace conflict? If appears that if we would change the namespace in the loaded XML document to the one we are using in our schema, the conflict is resolved. A first attempt may be:
// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Change namespace to reflect schema namespace
source.Root.SetAttributeValue("xmlns", "http://my.namespace");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
If we run this, the validation error is still not thrown, so setting the namespace attribute is not enough. The reason is that once the XDocument is loaded, every element in the tree gets prefixed with the namespace name. So we need to change them all, and so I wrote the following method that does this:
static void Main(string[] args)
{
// Load the xml document
XDocument source = XDocument.Load(@"instance.xml");
// Change namespace to reflect schema namespace
source = SetNamespace(source,"http://my.namespace");
// Load the schema
XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
xmlSchemaSet.Add(null, XmlReader.Create(@"customer.xsd"));
// Validate
try { source.Validate(xmlSchemaSet, ValidationCallback, true); }
catch (Exception ex) { Console.WriteLine(ex.Message); }
}
public static XDocument SetNamespace(XDocument source, XNamespace xNamespace)
{
foreach (XElement xElement in source.Descendants())
{
// First make sure that the xmlns-attribute is changed
xElement.SetAttributeValue("xmlns", xNamespace.NamespaceName);
// Then also prefix the name of the element with the namespace
xElement.Name = xNamespace + xElement.Name.LocalName;
}
return source;
}
static void ValidationCallback(object sender,
System.Xml.Schema.ValidationEventArgs e)
{
Console.WriteLine(string.Format("[{0}] {1}", e.Severity, e.Message));
}
The SetNameSpace method will set the corrrect namespace for each element in the XDocument. And if we run it now, the validation error is thrown again because the namespace in the XDocument has been modified and matches the schema namespace.
Parsing large XML filesIn "C#"
Strategy patternIn "C#"
A reference architecture (part 7)In "Architecture"
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有