Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Reading XML from a file that matches a schema

Status
Not open for further replies.

timmay3141

Programmer
Dec 3, 2002
468
US
I created an XSD schema for a data object, and I ran the Visual Studio xsd tool to generate classes based on that schema. All of that looks fine, but now I want to read the XML file which conforms to that schema and return an object of the corresponding class. I've done a lot of searching online but I haven't found anything that describes how to do this easily. I've done this kind of thing in Java before and it was easy, but never in C#, and I'd imagine that people need to do this often enough that this functionality exists. Can someone tell me what I need to do? Thanks.
 
The way I do this is to use System.XML there are loads of document and reader/writer classes to read xml files.

One thing to be aware of is namespaces when checking element names, I'ce come a cropper with this a few times before.

I have one xml file that I use to hold default values for properties so they aren't hardcoded and can be changed by users over time, here's the code for setting the default values that I use:

Code:
Public Sub SetDefaultValues(ByVal targetObject As Object)

        'If the filename isn't set exit the method
        If Me.FileName.Trim.Length = 0 Then Exit Sub

        'Collect the property descriptors for the object
        Dim propDescriptors As System.ComponentModel.PropertyDescriptorCollection
        propDescriptors = System.ComponentModel.TypeDescriptor.GetProperties( _
                                                                targetObject.GetType)

        'Read the xml file into an xml document
        Dim xmlDoc As New System.Xml.XmlDocument
        xmlDoc.Load(Me.FullDataPath)

        'Create a namespace manager
        Dim nmspMgr As New Xml.XmlNamespaceManager(xmlDoc.NameTable)

        'Loop through all of the children of the xml document and collect the
        'namespaces into the namespace manager
        Dim rootChild As System.Xml.XmlNode
        For Each rootChild In xmlDoc.ChildNodes
            Dim childAttribute As System.Xml.XmlAttribute
            If Not rootChild.Attributes Is Nothing Then
                For Each childAttribute In rootChild.Attributes
                    If childAttribute.Name.StartsWith("xmlns") Then
                        If childAttribute.Name = "xmlns" Then
                            nmspMgr.AddNamespace("", childAttribute.InnerText)
                        Else
                            'Extract the prefix
                            Dim prefix As String
                            prefix = Mid(childAttribute.Name, 7)
                            nmspMgr.AddNamespace(prefix, childAttribute.InnerText)
                        End If
                    End If
                Next
            End If
        Next

        'Collect the the namespace prefix that matches the XMLDataNamespace
        Dim nsPrefix As String
        nsPrefix = nmspMgr.LookupPrefix(My.Settings.XMLDataNamespace)

        'If the prefix is not empty add a colon to the end of it
        If nsPrefix.Trim.Length > 0 Then nsPrefix += ":"

        'Collect the default value nodes
        Dim defaultNodeList As System.Xml.XmlNodeList
        defaultNodeList = xmlDoc.SelectNodes("//" & nsPrefix & "DefaultValue", nmspMgr)

        'Loop through each node in the list setting the property value
        Dim currentNode As System.Xml.XmlElement
        For Each currentNode In defaultNodeList
            'Collect the child nodes into two variables
            Dim propertyName As String = ""
            Dim defaultValue As Object = Nothing
            Dim childNode As System.Xml.XmlElement
            For Each childNode In currentNode.ChildNodes
                Select Case childNode.Name
                    Case Is = nsPrefix & "Property"
                        propertyName = childNode.InnerText
                    Case Is = nsPrefix & "Value"
                        defaultValue = childNode.InnerText
                End Select
            Next

            'Collect the property descriptor
            Dim currentProperty As System.ComponentModel.PropertyDescriptor
            currentProperty = propDescriptors.Find(propertyName, True)

            'Set the value
            If Not currentProperty Is Nothing Then
                Dim propType As System.Type = currentProperty.GetType
                Select Case currentProperty.PropertyType.FullName
                    Case Is = "System.Decimal"
                        defaultValue = System.Convert.ToDecimal(defaultValue)
                    Case Is = "System.String"
                        defaultValue = System.Convert.ToString(defaultValue)
                    Case Is = "System.Int16"
                        defaultValue = System.Convert.ToInt16(defaultValue)
                    Case Is = "System.Int32"
                        defaultValue = System.Convert.ToInt32(defaultValue)
                    Case Is = "System.Int64"
                        defaultValue = System.Convert.ToInt64(defaultValue)
                    Case Is = "System.Byte"
                        defaultValue = System.Convert.ToByte(defaultValue)
                    Case Is = "System.Date"
                        defaultValue = System.Convert.ToDateTime(defaultValue)
                End Select

                currentProperty.SetValue(targetObject, defaultValue)
            End If

        Next

    End Sub

There are a couple of notes to this:

1. I store the path to the xml file in application settings.
2. I store the namespace for the elements in application settings.

I hope this helps.
 
Also, here's the schema:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="[URL unfurl="true"]http://www.flowguard.com/AccessSupplyChainInterface/ns"[/URL] xmlns:xs="[URL unfurl="true"]http://www.w3.org/2001/XMLSchema"[/URL] xmlns:asci="[URL unfurl="true"]http://www.flowguard.com/AccessSupplyChainInterface/ns"[/URL] targetNamespace="[URL unfurl="true"]http://www.flowguard.com/AccessSupplyChainInterface/ns"[/URL] elementFormDefault="qualified" attributeFormDefault="unqualified">
	<xs:element name="DefaultValues">
		<xs:annotation>
			<xs:documentation>A collection of default values.</xs:documentation>
		</xs:annotation>
		<xs:complexType>
			<xs:sequence>
				<xs:element name="DefaultValue" maxOccurs="unbounded">
					<xs:annotation>
						<xs:documentation>The specific default value</xs:documentation>
					</xs:annotation>
					<xs:complexType>
						<xs:sequence>
							<xs:element name="Property">
								<xs:annotation>
									<xs:documentation>The name of the property for which the default value applies</xs:documentation>
								</xs:annotation>
							</xs:element>
							<xs:element name="Value">
								<xs:annotation>
									<xs:documentation>The value for the property</xs:documentation>
								</xs:annotation>
							</xs:element>
						</xs:sequence>
					</xs:complexType>
				</xs:element>
			</xs:sequence>
		</xs:complexType>
	</xs:element>
</xs:schema>
 
Just remembered, the code is vb.net (i use vb at work and c# at home), but the class strucures are the same, the syntax is just slightly different.
 
Apparently it can do everything I already wanted, it just took a couple more hours of searching (and using the right combination of words...)

For anyone interested, here's all you need to do:

Code:
XmlSerializer ser = new XmlSerializer(typeof(MyXmlClass));
TextReader reader = new StreamReader(file);
MyXmlClass myDataObject = (MyXmlClass)ser.Deserialize(reader);

This is even more simple than I would have expected!
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top