I had an XML file that controlled much of my application and I wanted to be able to validate it and get some Intellisense to making edits a lot easier.  I  used XSD validation which works great for my design-time needs, but note that the Silverlight libraries don’t have the code to validate it at runtime.  This is fine for my current situation, but may be a problem later.

Here is how I got it working:

1. Add an XSD to the project (Add New Item -> XML Schema)

2. Visual Studio 2010 has a lot of tools for visualizing and reporting on the schema, but I found it easiest just to go to the editor by clicking "Use the XML Editor to view and edit the underlying XML schema file"

3. Build the XSD. Since I was new to XSD, a couple sites were pretty helpful. http://www.learn-xml-schema-tutorial.com/ and http://www.w3schools.com/Schema/ Below is a very simplistic XSD that requires the XML have a list of customers. Each customer will have a customerType attribute that has to be "internal" or "external", an optional firstName, and a required lastName.

Code Snippet
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <xs:schema id="MySchema"
  3.     targetNamespace="http://tempuri.org/MySchema.xsd"
  4.     elementFormDefault="qualified"
  5.     xmlns="http://tempuri.org/MySchema.xsd"
  6.     xmlns:mstns="http://tempuri.org/MySchema.xsd"
  7.     xmlns:xs="http://www.w3.org/2001/XMLSchema"
  8. >
  9.   <xs:element name="customers">
  10.     <xs:complexType>
  11.       <xs:sequence>
  12.         <xs:element name="customer" minOccurs="0" maxOccurs="unbounded">
  13.           <xs:complexType>
  14.             <xs:attribute name="customerType" use="required">
  15.               <xs:simpleType>
  16.                 <xs:restriction base="xs:string">
  17.                   <xs:enumeration value="internal" />
  18.                   <xs:enumeration value="external" />
  19.                 </xs:restriction>
  20.               </xs:simpleType>
  21.             </xs:attribute>
  22.             <xs:attribute name="firstName" use="optional" />
  23.             <xs:attribute name="lastName" use="required" />
  24.           </xs:complexType>
  25.           
  26.         </xs:element>
  27.       </xs:sequence>
  28.     </xs:complexType>
  29.     
  30.   </xs:element>
  31. </xs:schema>

4. On your XML file, go to the Properties window and edit Schemas.  You’ll need to browse to where your XSD is to associate it.

image

5.  Edit the root node of the XML file to use the right default namespace

<?xml version="1.0" encoding="utf-8" ?>
<customers xmlns="http://tempuri.org/MySchema.xsd">
</customers>

6.  Now Intellisense will work and help you enter the right values.  You’ll get compiler warnings if your XML does not conform to the schema

image

7.  There’s one complication with this – if you want to read the XML file in as an XDocument, you’ll now have to include the namespace when referencing the Nodes.  For example,  doc.Descendents(XName.Get(“customers”,”http://tempuri.org/MySchema.xsd”)).  To avoid this, I used some code from http://www.everydayinternetstuff.com/2009/12/default-namespace-for-linq-to-xml-query/ that will just strip the namespace info from the XML. 

Code Snippet
  1. /// <summary>
  2. /// Removes any namespaces prefixes from elements so we don't have
  3. /// to worry about them when querying.  The namespace is there for XSD validation before
  4. /// coming to us, but we really don't care about it.
  5. /// </summary>
  6. /// <param name="xdoc"></param>
  7. private void RemoveNamespace(XDocument xdoc)
  8. {
  9.     foreach (XElement e in xdoc.Root.DescendantsAndSelf())
  10.     {
  11.         if (e.Name.Namespace != XNamespace.None)
  12.         {
  13.             e.Name = XNamespace.None.GetName(e.Name.LocalName);
  14.         }
  15.  
  16.         if (e.Attributes().Where(a => a.IsNamespaceDeclaration || a.Name.Namespace != XNamespace.None).Any())
  17.         {
  18.             e.ReplaceAttributes(e.Attributes().Select
  19.                 (a => a.IsNamespaceDeclaration ? null : a.Name.Namespace != XNamespace.None ? new XAttribute(XNamespace.None.GetName(a.Name.LocalName), a.Value) : a));
  20.         }
  21.     }
  22.  
  23. }