Adding XmlNode to Silverlight
Of the many types of data I work with, XML shows up all the time. However, when working in Silverlight, your XML options are dramatically limited. For example, for some reason Microsoft totally forgot to add a simple XmlNode class. Microsoft seems to think that using LINQ for XML will solve all our XML problems. Well, most of the stuff that I do on a daily basis has nothing to do with extensive querying XML. I just need something simple to pull quick data. Not only that, but LINQ for XML is in the System.Xml.Linq assembly, which is not a native Silverlight assembly. I’m not going to bloat my XAP file for a single dependency.
So, in my internal build of Themelia for Silverlight, you will find the Themelia.Xml.XmlNode class. I use this all over the build entire XML trees and to search the same tree for elements I need. All this in one small discrete class that doesn’t require you to bloat your XAP file. Here’s a sample of how to use my XmlNode class:
XmlReader reader = XmlReader.Create(new StringReader(@"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/""> <s:Body> <s:Fault> <faultcode>s:Client</faultcode> <faultstring xml:lang=""en-US"">This is the fault reason.</faultstring> <detail> <FaultDetail xmlns=""http://schemas.datacontract.org/2004/07/Sample.Service"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""> <Reason>This is the fault reason.</Reason> <Source>GetPersonData</Source> </FaultDetail> </detail> </s:Fault> </s:Body> </s:Envelope>")); XmlNode node = new XmlNode(reader); XmlNode faultDetailNode = node.FindDescendant("faultDetail"); stackpanel01.Children.Add(new TextBlock { Text = faultDetailNode.FirstChild.TextContent }); stackpanel01.Children.Add(new TextBlock { Text = faultDetailNode.AttributeDictionary["xmlns"] }); stackpanel01.Children.Add(new TextBlock { Text = faultDetailNode.AttributeDictionary["xmlns:i"] });
It does a few more things that you can see by looking at the source for it, but this is essentially all I need in for XML processing on most days. Here’s the source for XmlNode.cs:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; //+ namespace Themelia.Xml { public class XmlNode { //- @Name -// public String Name { get; set; } //- @TextContent -// public String TextContent { get; set; } //- @Children -// public List<XmlNode> NodeList { get; set; } //- @AttributeMap -// public Dictionary<String, String> AttributeDictionary { get; set; } //- @FirstChild -// public XmlNode FirstChild { get { if (NodeList.Count > 0) { return NodeList[0]; } //+ return null; } } //+ //- @Ctor -// private XmlNode() { NodeList = new List<XmlNode>(); AttributeDictionary = new Dictionary<String, String>(); } public XmlNode(XmlReader xmlReader) : this() { Initialize(xmlReader); } public XmlNode(Stream xmlStream) : this(XmlReader.Create(xmlStream)) { } public XmlNode(String name) : this() { Name = name; } //- $Initialize -// private void Initialize(XmlReader reader) { reader.Read(); this.Name = reader.Name; //+ attribute reader.MoveToElement(); if (reader.HasAttributes) { reader.MoveToFirstAttribute(); AttributeDictionary.Add(reader.Name, reader.Value); while (reader.MoveToNextAttribute()) { AttributeDictionary.Add(reader.Name, reader.Value); } } //+ node while (reader.Read()) { if (reader.NodeType == XmlNodeType.Element) { NodeList.Add(new XmlNode(reader.ReadSubtree())); } else if (reader.NodeType == XmlNodeType.Text) { TextContent = reader.ReadContentAsString(); } } } //- @FindDescendant -// public XmlNode FindDescendant(String name) { XmlNode foundNode = NodeList.FirstOrDefault(p => p.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); if (foundNode != null) { return foundNode; } foreach (XmlNode node in NodeList) { foundNode = node.FindDescendant(name); if (foundNode != null) { return foundNode; } } //+ return foundNode; } //- @FindDescendantList -// public List<XmlNode> FindDescendantList(String name) { List<XmlNode> nodeList = new List<XmlNode>(); XmlNode foundNode = NodeList.FirstOrDefault(p => p.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)); if (foundNode != null) { nodeList.Add(foundNode); foundNode = null; } foreach (XmlNode node in NodeList) { foundNode = node.FindDescendant(name); if (foundNode != null) { nodeList.Add(foundNode); foundNode = null; } } //+ return nodeList; } //- @GetChildDictionary -// public Dictionary<String, String> GetChildDictionary() { Dictionary<String, String> map = new Dictionary<String, String>(); foreach (XmlNode node in NodeList) { if (!String.IsNullOrEmpty(node.TextContent)) { map.Add(node.Name, node.TextContent); } } //+ return map; } } }
Yeah, there’s more it could probably do and I’ll probably add more to it in the future, but this little class has worked in all my XML scenarios so far.