XmlDataLoader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / Data / System / Data / XmlDataLoader.cs / 2 / XmlDataLoader.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// [....] 
// [....]
// [....] 
//----------------------------------------------------------------------------- 

namespace System.Data { 
    using System;
    using System.Collections;
    using System.Data.Common;
    using System.Diagnostics; 
    using System.Globalization;
    using System.Text; 
    using System.Xml; 
    using System.Xml.Serialization;
 
    internal sealed class XmlDataLoader {
        DataSet dataSet;
        XmlToDatasetMap nodeToSchemaMap = null;
        Hashtable       nodeToRowMap; 
        Stack           childRowsStack = null;
        Hashtable       htableExcludedNS = null; 
        bool    fIsXdr = false; 
        internal bool    isDiffgram = false;
 
        DataRow topMostRow = null;

        XmlElement topMostNode = null;
        bool ignoreSchema = false; 

        DataTable dataTable; 
        bool isTableLevel = false; 
        private bool fromInference = false;
 
        internal XmlDataLoader( DataSet dataset, bool IsXdr, bool ignoreSchema) {
            // Initialization
            this.dataSet = dataset;
            this.nodeToRowMap = new Hashtable(); 
            this.fIsXdr = IsXdr;
            this.ignoreSchema = ignoreSchema; 
        } 

        internal XmlDataLoader( DataSet dataset, bool IsXdr, XmlElement topNode, bool ignoreSchema) { 
            // Initialization
            this.dataSet = dataset;
            this.nodeToRowMap = new Hashtable();
            this.fIsXdr = IsXdr; 

            // Allocate the stack and create the mappings 
            childRowsStack = new Stack(50); 

            topMostNode = topNode; 
            this.ignoreSchema = ignoreSchema;
        }

        internal XmlDataLoader( DataTable datatable, bool IsXdr, bool ignoreSchema) { 
            // Initialization
            this.dataSet = null; 
            dataTable = datatable; 
            isTableLevel = true;
            this.nodeToRowMap = new Hashtable(); 
            this.fIsXdr = IsXdr;
            this.ignoreSchema = ignoreSchema;
        }
 
        internal XmlDataLoader( DataTable datatable, bool IsXdr, XmlElement topNode, bool ignoreSchema) {
            // Initialization 
            this.dataSet = null; 
            dataTable = datatable;
            isTableLevel = true; 
            this.nodeToRowMap = new Hashtable();
            this.fIsXdr = IsXdr;

            // Allocate the stack and create the mappings 

            childRowsStack = new Stack(50); 
            topMostNode = topNode; 
            this.ignoreSchema = ignoreSchema;
        } 

        internal bool FromInference {
            get {
                return fromInference; 
            }
            set { 
                fromInference = value; 
            }
        } 

        // after loading, all detached DataRows are attached to their tables
        private void AttachRows( DataRow parentRow, XmlNode parentElement ) {
            if (parentElement == null) 
                return;
 
            for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) { 
                if (n.NodeType == XmlNodeType.Element) {
                    XmlElement e = (XmlElement) n; 
                    DataRow r = GetRowFromElement( e );
                    if (r != null && r.RowState == DataRowState.Detached) {
                        if (parentRow != null)
                            r.SetNestedParentRow( parentRow, /*setNonNested*/ false ); 

                        r.Table.Rows.Add( r ); 
                    } 
                    else if (r == null) {
                        // n is a 'sugar element' 
                        AttachRows( parentRow, n );
                    }

                    // attach all detached rows 
                    AttachRows( r, n );
                } 
            } 
        }
 
        private int CountNonNSAttributes (XmlNode node) {
            int count = 0;
            for (int i = 0; i < node.Attributes.Count; i++) {
                XmlAttribute attr = node.Attributes[i]; 
                if (!FExcludedNamespace(node.Attributes[i].NamespaceURI))
                        count++; 
            } 
            return count;
        } 

        private string GetValueForTextOnlyColums( XmlNode n ) {
            string value = null;
 
            // don't consider whitespace
            while (n != null && (n.NodeType == XmlNodeType.Whitespace || !IsTextLikeNode(n.NodeType))) { 
                n = n.NextSibling; 
            }
 
            if (n != null) {
                if (IsTextLikeNode( n.NodeType ) && (n.NextSibling == null || !IsTextLikeNode( n.NodeType ))) {
                    // don't use string builder if only one text node exists
                    value = n.Value; 
                    n = n.NextSibling;
                } 
                else { 
                    StringBuilder sb = new StringBuilder();
                    while (n != null && IsTextLikeNode( n.NodeType )) { 
                        sb.Append( n.Value );
                        n = n.NextSibling;
                    }
                    value = sb.ToString(); 
                }
            } 
 
            if (value == null)
                value = String.Empty; 

            return value;
        }
 
        private string GetInitialTextFromNodes( ref XmlNode n ) {
            string value = null; 
 
            if (n != null) {
                // don't consider whitespace 
                while (n.NodeType == XmlNodeType.Whitespace)
                    n = n.NextSibling;

                if (IsTextLikeNode( n.NodeType ) && (n.NextSibling == null || !IsTextLikeNode( n.NodeType ))) { 
                    // don't use string builder if only one text node exists
                    value = n.Value; 
                    n = n.NextSibling; 
                }
                else { 
                    StringBuilder sb = new StringBuilder();
                    while (n != null && IsTextLikeNode( n.NodeType )) {
                        sb.Append( n.Value );
                        n = n.NextSibling; 
                    }
                    value = sb.ToString(); 
                } 
            }
 
            if (value == null)
                value = String.Empty;

            return value; 
        }
 
        private DataColumn GetTextOnlyColumn( DataRow row ) { 
            DataColumnCollection columns = row.Table.Columns;
            int cCols = columns.Count; 
            for (int iCol = 0; iCol < cCols; iCol++) {
                DataColumn c = columns[iCol];
                if (IsTextOnly( c ))
                    return c; 
            }
            return null; 
        } 

        internal DataRow GetRowFromElement( XmlElement e ) { 
            return(DataRow) nodeToRowMap[e];
        }

        internal bool FColumnElement(XmlElement e) { 
            if (nodeToSchemaMap.GetColumnSchema(e, FIgnoreNamespace(e)) == null)
                return false; 
 
            if (CountNonNSAttributes(e) > 0)
                return false; 

            for (XmlNode tabNode = e.FirstChild; tabNode != null; tabNode = tabNode.NextSibling)
                if (tabNode is XmlElement)
                    return false; 

            return true; 
        } 

        private bool FExcludedNamespace(string ns) { 
            if (ns.Equals(Keywords.XSD_XMLNS_NS))
                return true;

            if (htableExcludedNS == null) 
                return false;
 
            return htableExcludedNS.Contains(ns); 
        }
 
        private bool FIgnoreNamespace(XmlNode node) {
            XmlNode ownerNode;
            if (!fIsXdr)
                return false; 
            if (node is XmlAttribute)
                ownerNode = ((XmlAttribute)node).OwnerElement; 
            else 
                ownerNode = node;
            if (ownerNode.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal)) 
                return true;
            else
                return false;
        } 

        private bool FIgnoreNamespace(XmlReader node) { 
            if (fIsXdr && node.NamespaceURI.StartsWith("x-schema:#", StringComparison.Ordinal)) 
                return true;
            else 
                return false;
        }

        internal bool IsTextLikeNode( XmlNodeType n ) { 
            switch (n) {
                case XmlNodeType.EntityReference: 
                    throw ExceptionBuilder.FoundEntity(); 

                case XmlNodeType.Text: 
                case XmlNodeType.Whitespace:
                case XmlNodeType.SignificantWhitespace:
                case XmlNodeType.CDATA:
                    return true; 

                default: 
                    return false; 
            }
        } 

        internal bool IsTextOnly( DataColumn c ) {
            if (c.ColumnMapping != MappingType.SimpleContent)
                return false; 
            else
                return true; 
        } 

        internal void LoadData( XmlDocument xdoc ) { 
            if (xdoc.DocumentElement == null)
                return;

            bool saveEnforce; 

            if (isTableLevel) { 
                saveEnforce = dataTable.EnforceConstraints; 
                dataTable.EnforceConstraints = false;
            } 
            else {
                saveEnforce = dataSet.EnforceConstraints;
                dataSet.EnforceConstraints = false;
                dataSet.fInReadXml = true; 
            }
 
            if (isTableLevel) { 
                nodeToSchemaMap = new XmlToDatasetMap(dataTable, xdoc.NameTable);
            } 
            else {
                nodeToSchemaMap = new XmlToDatasetMap(dataSet, xdoc.NameTable);
            }
/* 
            // Top level table or dataset ?
            XmlElement rootElement = xdoc.DocumentElement; 
            Hashtable tableAtoms = new Hashtable(); 
            XmlNode tabNode;
            if (CountNonNSAttributes (rootElement) > 0) 
                dataSet.fTopLevelTable = true;
            else {
                for (tabNode = rootElement.FirstChild; tabNode != null; tabNode = tabNode.NextSibling) {
                    if (tabNode is XmlElement && tabNode.LocalName != Keywords.XSD_SCHEMA) { 
                        object value = tableAtoms[QualifiedName (tabNode.LocalName, tabNode.NamespaceURI)];
                        if (value == null || (bool)value == false) { 
                            dataSet.fTopLevelTable = true; 
                            break;
                        } 
                    }
                }
            }
*/ 
            DataRow topRow = null;
            if (isTableLevel ||(dataSet!= null && dataSet.fTopLevelTable) ){ 
                XmlElement e = xdoc.DocumentElement; 
                DataTable topTable = (DataTable) nodeToSchemaMap.GetSchemaForNode(e, FIgnoreNamespace(e));
                if (topTable != null) { 
                    topRow = topTable.CreateEmptyRow(); //[....] perf
                    nodeToRowMap[ e ] = topRow;

                    // get all field values. 
                    LoadRowData( topRow, e );
                    topTable.Rows.Add(topRow); 
                } 
            }
 
            LoadRows( topRow, xdoc.DocumentElement );
            AttachRows( topRow, xdoc.DocumentElement );

 
            if (isTableLevel) {
                dataTable.EnforceConstraints = saveEnforce; 
            } 
            else {
                dataSet.fInReadXml = false; 
                dataSet.EnforceConstraints = saveEnforce;
            }

        } 

        private void LoadRowData(DataRow row, XmlElement rowElement) { 
            XmlNode n; 
            DataTable table = row.Table;
            if (FromInference) 
                table.Prefix = rowElement.Prefix;

            // keep a list of all columns that get updated
            Hashtable foundColumns = new Hashtable(); 

            row.BeginEdit(); 
 
            // examine all children first
            n = rowElement.FirstChild; 

            // Look for data to fill the TextOnly column
            DataColumn column = GetTextOnlyColumn( row );
            if (column != null) { 
                foundColumns[column] = column;
                string text = GetValueForTextOnlyColums( n ) ; 
                if (XMLSchema.GetBooleanAttribute(rowElement, Keywords.XSI_NIL, Keywords.XSINS, false) && Common.ADP.IsEmpty(text) ) 
                    row[column] = DBNull.Value;
                else 
                    SetRowValueFromXmlText( row, column, text );
            }

            // Walk the region to find elements that map to columns 
            while (n != null && n != rowElement) {
                if (n.NodeType == XmlNodeType.Element) { 
                    XmlElement e = (XmlElement) n; 

                    object schema = nodeToSchemaMap.GetSchemaForNode( e, FIgnoreNamespace(e) ); 
                    if (schema is DataTable) {
                        if (FColumnElement(e))
                            schema = nodeToSchemaMap.GetColumnSchema( e, FIgnoreNamespace(e) );
                    } 

                    // if element has its own table mapping, it is a separate region 
                    if (schema == null || schema is DataColumn) { 
                        // descend to examine child elements
                        n = e.FirstChild; 

                        if (schema != null && schema is DataColumn) {
                            DataColumn c = (DataColumn) schema;
 
                            if (c.Table == row.Table && c.ColumnMapping != MappingType.Attribute && foundColumns[c] == null) {
                                foundColumns[c] = c; 
                                string text = GetValueForTextOnlyColums( n ) ; 
                                if (XMLSchema.GetBooleanAttribute(e, Keywords.XSI_NIL, Keywords.XSINS, false) && Common.ADP.IsEmpty(text) )
                                    row[c] = DBNull.Value; 
                                else
                                    SetRowValueFromXmlText( row, c, text );
                            }
                        } 
                        else if ((schema == null) && (n!=null)) {
                            continue; 
                        } 

 
                        // nothing left down here, continue from element
                        if (n == null)
                            n = e;
                    } 
                }
 
                // if no more siblings, ascend back toward original element (rowElement) 
                while (n != rowElement && n.NextSibling == null) {
                    n = n.ParentNode; 
                }

                if (n != rowElement)
                    n = n.NextSibling; 
            }
 
            // 
            // Walk the attributes to find attributes that map to columns.
            // 
            foreach( XmlAttribute attr in rowElement.Attributes ) {
                object schema = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr) );
                if (schema != null && schema is DataColumn) {
                    DataColumn c = (DataColumn) schema; 

                    if (c.ColumnMapping == MappingType.Attribute && foundColumns[c] == null) { 
                        foundColumns[c] = c; 
                        n = attr.FirstChild;
                        SetRowValueFromXmlText( row, c, GetInitialTextFromNodes( ref n ) ); 
                    }
                }
            }
 
            // Null all columns values that aren't represented in the tree
            foreach( DataColumn c in row.Table.Columns ) { 
                if (foundColumns[c] == null && XmlToDatasetMap.IsMappedColumn(c)) { 
                    if (!c.AutoIncrement) {
                        if (c.AllowDBNull)  { 
                            row[c] = DBNull.Value;
                        }
                        else {
                            row[c] = c.DefaultValue; 
                        }
                    } 
                    else { 
                        c.Init(row.tempRecord);
                    } 
                }
            }

            row.EndEdit(); 
        }
 
 
        // load all data from tree structre into datarows
        private void LoadRows( DataRow parentRow, XmlNode parentElement ) { 
            if (parentElement == null)
                return;

            // Skip schema node as well 
            if (parentElement.LocalName == Keywords.XSD_SCHEMA && parentElement.NamespaceURI == Keywords.XSDNS ||
                parentElement.LocalName == Keywords.SQL_SYNC   && parentElement.NamespaceURI == Keywords.UPDGNS || 
                parentElement.LocalName == Keywords.XDR_SCHEMA && parentElement.NamespaceURI == Keywords.XDRNS) 
                return;
 
            for (XmlNode n = parentElement.FirstChild; n != null; n = n.NextSibling) {
                if (n is XmlElement) {
                    XmlElement e = (XmlElement) n;
                    object schema = nodeToSchemaMap.GetSchemaForNode( e, FIgnoreNamespace(e) ); 

                    if (schema != null && schema is DataTable) { 
                        DataRow r = GetRowFromElement( e ); 
                        if (r == null) {
                            // skip columns which has the same name as another table 
                            if (parentRow != null && FColumnElement(e))
                                continue;

                            r = ((DataTable)schema).CreateEmptyRow(); 
                            nodeToRowMap[ e ] = r;
 
                            // get all field values. 
                            LoadRowData( r, e );
                        } 

                        // recurse down to inner elements
                        LoadRows( r, n );
                    } 
                    else {
                        // recurse down to inner elements 
                        LoadRows( null, n ); 
                    }
                } 
            }
        }

        private void SetRowValueFromXmlText( DataRow row, DataColumn col, string xmlText ) { 
            row[col] = col.ConvertXmlToObject(xmlText);
        } 
 
        internal void LoadTopMostRow(ref bool[] foundColumns) {
            // Attempt to load row from top node we backed up in DataSet.ReadXml() 
            // In most cases it contains the DataSet name and no information

            // Check if DataSet object matches the top node (it won't in most cases)
 
            Object obj = nodeToSchemaMap.GetSchemaForNode(topMostNode,FIgnoreNamespace(topMostNode));
 
            if (obj is DataTable) {                         // It's a table? Load it. 
                DataTable table = (DataTable) obj;
 
                topMostRow = table.CreateEmptyRow();

                foundColumns = new bool[topMostRow.Table.Columns.Count];
 
                //
                // Walk the attributes to find attributes that map to columns. 
                // 
                foreach( XmlAttribute attr in topMostNode.Attributes ) {
                    object schema = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr) ); 

                    if (schema != null && schema is DataColumn) {
                        DataColumn c = (DataColumn) schema;
 
                        if (c.ColumnMapping == MappingType.Attribute) {
                            XmlNode n = attr.FirstChild; 
                            SetRowValueFromXmlText( topMostRow, c, GetInitialTextFromNodes( ref n ) ); 
                            foundColumns[c.Ordinal] = true;
                        } 
                    }
                }

            } 
            topMostNode = null;
        } 
 
        private XmlReader dataReader = null;
        private object XSD_XMLNS_NS; 
        private object XDR_SCHEMA;
        private object XDRNS;
        private object SQL_SYNC;
        private object UPDGNS; 
        private object XSD_SCHEMA;
        private object XSDNS; 
 
        private object DFFNS;
        private object MSDNS; 
        private object DIFFID;
        private object HASCHANGES;
        private object ROWORDER;
 
        private void InitNameTable() {
            XmlNameTable nameTable = dataReader.NameTable; 
 
            XSD_XMLNS_NS = nameTable.Add(Keywords.XSD_XMLNS_NS);
            XDR_SCHEMA = nameTable.Add(Keywords.XDR_SCHEMA); 
            XDRNS = nameTable.Add(Keywords.XDRNS);
            SQL_SYNC = nameTable.Add(Keywords.SQL_SYNC);
            UPDGNS = nameTable.Add(Keywords.UPDGNS);
            XSD_SCHEMA = nameTable.Add(Keywords.XSD_SCHEMA); 
            XSDNS = nameTable.Add(Keywords.XSDNS);
 
            DFFNS = nameTable.Add(Keywords.DFFNS); 
            MSDNS = nameTable.Add(Keywords.MSDNS);
            DIFFID = nameTable.Add(Keywords.DIFFID); 
            HASCHANGES = nameTable.Add(Keywords.HASCHANGES);
            ROWORDER = nameTable.Add(Keywords.ROWORDER);
        }
 
        internal void LoadData(XmlReader reader) {
            dataReader = DataTextReader.CreateReader(reader); 
 
            int entryDepth = dataReader.Depth;                  // Store current XML element depth so we'll read
                                                                // correct portion of the XML and no more 
            bool fEnforce = isTableLevel ? dataTable.EnforceConstraints : dataSet.EnforceConstraints;
                                                                // Keep constraints status for datataset/table
            InitNameTable();                                    // Adds DataSet namespaces to reader's nametable
 
            if (nodeToSchemaMap == null) {                      // Create XML to dataset map
                nodeToSchemaMap = isTableLevel ? new XmlToDatasetMap(dataReader.NameTable, dataTable) : 
                                                 new XmlToDatasetMap(dataReader.NameTable, dataSet); 
            }
 
            if (isTableLevel) {
                dataTable.EnforceConstraints = false;           // Disable constraints
            }
            else { 
                dataSet.EnforceConstraints = false;             // Disable constraints
                dataSet.fInReadXml = true;                      // We're in ReadXml now 
            } 

            if (topMostNode != null) {                          // Do we have top node? 

                if (!isDiffgram && !isTableLevel) {             // Not a diffgram  and not DataSet?
                    DataTable table = nodeToSchemaMap.GetSchemaForNode(topMostNode, FIgnoreNamespace(topMostNode)) as DataTable;
                                                                // Try to match table in the dataset to this node 
                    if (table != null) {                        // Got the table ?
                        LoadTopMostTable(table);                // Load top most node 
                    } 
                }
 
                topMostNode = null;                             // topMostNode is no more. Good riddance.
            }

            while( !dataReader.EOF ) {                          // Main XML parsing loop. Check for EOF just in case. 
                if (dataReader.Depth < entryDepth)              // Stop if we have consumed all elements allowed
                    break; 
 
                if ( reader.NodeType != XmlNodeType.Element ) { // Read till Element is found
                    dataReader.Read(); 
                    continue;
                }
                DataTable table = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader));
                                                                // Try to get table for node 
                if (table == null) {                            // Read till table is found
 
                    if (!ProcessXsdSchema())                    // Check for schemas... 
                        dataReader.Read();                      // Not found? Read next element.
 
                    continue;
                }

                LoadTable(table,  false /* isNested */);        // Here goes -- load data for this table 
                                                                // This is a root table, so it's not nested
            } 
 
            if (isTableLevel) {
                dataTable.EnforceConstraints = fEnforce;        // Restore constraints and return 
            }
            else {
                dataSet.fInReadXml = false;                     // We're done.
                dataSet.EnforceConstraints = fEnforce;          // Restore constraints and return 
            }
        } 
 
        // Loads a top most table.
        // This is neded because desktop is capable of loading almost anything into the dataset. 
        // The top node could be a DataSet element or a Table element. To make things worse,
        // you could have a table with the same name as dataset.
        // Here's how we're going to dig into this mess:
        // 
        //                                 TopNode is null ?
        //                                / No           \ Yes 
        //                  Table matches TopNode ?       Current node is the table start 
        //                 / No                  \ Yes (LoadTopMostTable called in this case only)
        //   Current node is the table start    DataSet name matches one of the tables ? 
        //      TopNode is dataset node        / Yes                                  \ No
        //                                    /                                        TopNode is the table
        //      Current node matches column or nested table in the table ?             and current node
        //     / No                                                 \ Yes              is a column or a 
        //     TopNode is DataSet                            TopNode is table          nested table
        // 
        // Yes, it is terrible and I don't like it also.. 

        private void LoadTopMostTable(DataTable table) { 

            //        /------------------------------- This one is in topMostNode (backed up to XML DOM)
            //   /----------------------------- We are here on entrance
            //      Value 
            //      Value
            //  
... // \------------------------------ We are here on exit Debug.Assert (table != null, "Table to be loaded is null on LoadTopMostTable() entry"); Debug.Assert (topMostNode != null, "topMostNode is null on LoadTopMostTable() entry"); Debug.Assert (!isDiffgram, "Diffgram mode is on while we have topMostNode table. This is bad." ); bool topNodeIsTable = isTableLevel || (dataSet.DataSetName != table.TableName); // If table name we have matches dataset // name top node could be a DataSet OR a table. // It's a table overwise. DataRow row = null; // Data row we're going to add to this table bool matchFound = false; // Assume we found no matching elements int entryDepth = dataReader.Depth - 1; // Store current reader depth so we know when to stop reading // Adjust depth by one as we've read top most element // outside this method. string textNodeValue; // Value of a text node we might have Debug.Assert (entryDepth >= 0, "Wrong entry Depth for top most element." ); int entryChild = childRowsStack.Count; // Memorize child stack level on entry DataColumn c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; // This is the columns data we might find XmlNode n; // Need this to pass by reference foreach( XmlAttribute attr in topMostNode.Attributes ) { // Check all attributes in this node c = nodeToSchemaMap.GetColumnSchema( attr, FIgnoreNamespace(attr)) as DataColumn; // Try to mach attribute to column if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) { // If it's a column with attribute mapping n = attr.FirstChild; foundColumns[c.Ordinal] = c.ConvertXmlToObject(GetInitialTextFromNodes(ref n)); // Get value matchFound = true; // and note we found a matching element } } // Now handle elements. This could be columns or nested tables // We'll skip the rest as we have no idea what to do with it. // Note: we do not need to read first as we're already as it has been done by caller. while (entryDepth < dataReader.Depth ) { switch (dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element object o = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? if ( c != null ) { // Do we have matched column in this table? // Let's load column data if (foundColumns[c.Ordinal] == null) { // If this column was not found before LoadColumn (c, foundColumns); // Get column value. matchFound = true; // Got matched row. } else { dataReader.Read(); // Advance to next element. } } else { DataTable nestedTable = o as DataTable; // Perhaps, it's a nested table ? if ( nestedTable != null ) { // Do we have matched table in DataSet ? LoadTable (nestedTable, true /* isNested */); // Yes. Load nested table (recursive) matchFound = true; // Got matched nested table } else if (ProcessXsdSchema()) { // Check for schema. Skip or load if found. continue; // Schema has been found. Process the next element // we're already at (done by schema processing). } else { // Not a table or column in this table ? if (!(matchFound || topNodeIsTable)) { // Could top node be a DataSet? return; // Assume top node is DataSet // and stop top node processing } dataReader.Read(); // Continue to the next element. } } break; // Oops. Not supported case XmlNodeType.EntityReference: // Oops. No support for Entity Reference throw ExceptionBuilder.FoundEntity(); case XmlNodeType.Text: // It looks like a text. case XmlNodeType.Whitespace: // This actually could be case XmlNodeType.CDATA: // if we have XmlText in our table case XmlNodeType.SignificantWhitespace: textNodeValue = dataReader.ReadString(); // Get text node value. c = table.xmlText; // Get XML Text column from our table if (c != null && foundColumns[c.Ordinal] == null) { // If XmlText Column is set // and we do not have data already foundColumns[c.Ordinal] = c.ConvertXmlToObject(textNodeValue); // Read and store the data } break; default: dataReader.Read(); // We don't process that, skip to the next element. break; } } dataReader.Read(); // Proceed to the next element. // It's the time to populate row with loaded data and add it to the table we'we just read to the table for ( int i = foundColumns.Length -1; i >= 0; --i) { // Check all columns if (null == foundColumns[i]) { // Got data for this column ? c = collection[i]; // No. Get column for this index if (c.AllowDBNull && c.ColumnMapping != MappingType.Hidden && !c.AutoIncrement) { foundColumns[i] = DBNull.Value; // Assign DBNull if possible // table.Rows.Add() below will deal // with default values and autoincrement } } } row = table.Rows.AddWithColumnEvents(foundColumns); // Create, populate and add row while (entryChild < childRowsStack.Count) { // Process child rows we might have DataRow childRow = (DataRow) childRowsStack.Pop(); // Get row from the stack bool unchanged = (childRow.RowState == DataRowState.Unchanged); // Is data the same as before? childRow.SetNestedParentRow(row, /*setNonNested*/ false); // Set parent row if (unchanged) // Restore record if child row's unchanged childRow.oldRecord = childRow.newRecord; } } // Loads a table. // Yes, I know it's a big method. This is done to avoid performance penalty of calling methods // with many arguments and to keep recursion within one method only. To make code readable, // this method divided into 3 parts: attribute processing (including diffgram), // nested elements processing and loading data. Please keep it this way. private void LoadTable(DataTable table, bool isNested ) { // /--------------------------- We are here on entrance // // Value // Value //
/-------------------------- We are here on exit // // ... // // ... //
Debug.Assert (table != null, "Table to be loaded is null on LoadTable() entry"); DataRow row = null; // Data row we're going to add to this table int entryDepth = dataReader.Depth; // Store current reader depth so we know when to stop reading int entryChild = childRowsStack.Count; // Memorize child stack level on entry DataColumn c; // Hold column here DataColumnCollection collection = table.Columns; // Hold column collectio here object[] foundColumns = new object[collection.Count]; // This is the columns data we found // This is used to process diffgramms int rowOrder = -1; // Row to insert data to string diffId = String.Empty; // Diffgram ID string string hasChanges = null; // Changes string bool hasErrors = false; // Set this in case of problem string textNodeValue; // Value of a text node we might have // Process attributes first for ( int i = dataReader.AttributeCount -1; i >= 0; --i) { // Check all attributes one by one dataReader.MoveToAttribute(i); // Get this attribute c = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader)) as DataColumn; // Try to get column for this attribute if ((c != null) && (c.ColumnMapping == MappingType.Attribute)) { // Yep, it is a column mapped as attribute // Get value from XML and store it in the object array foundColumns[c.Ordinal] = c.ConvertXmlToObject(dataReader.Value); } // Oops. No column for this element // // else if (table.XmlText != null && // dataReader.NamespaceURI == Keywords.XSINS && // dataReader.LocalName == Keywords.XSI_NIL ) { // // Got XMLText column and it's a NIL attribute? // if (XmlConvert.ToBoolean(dataReader.Value)) { // // If NIL attribute set to true... // // Assign DBNull to XmlText column // foundColumns[table.XmlText.Ordinal] = DBNull.Value; // } // } if ( isDiffgram ) { // Now handle some diffgram attributes if ( dataReader.NamespaceURI == Keywords.DFFNS ) { switch (dataReader.LocalName) { case Keywords.DIFFID: // Is it a diffgeam ID ? diffId = dataReader.Value; // Store ID break; case Keywords.HASCHANGES: // Has chages attribute ? hasChanges = dataReader.Value; // Store value break; case Keywords.HASERRORS: // Has errors attribute ? hasErrors = (bool)Convert.ChangeType(dataReader.Value, typeof(bool), CultureInfo.InvariantCulture); // Store value break; } } else if ( dataReader.NamespaceURI == Keywords.MSDNS ) { if ( dataReader.LocalName == Keywords.ROWORDER ) { // Is it a row order attribute ? rowOrder = (Int32)Convert.ChangeType(dataReader.Value, typeof(Int32), CultureInfo.InvariantCulture); // Store it } else if (dataReader.LocalName.StartsWith("hidden", StringComparison.Ordinal)) { // Hidden column ? c = collection[XmlConvert.DecodeName(dataReader.LocalName.Substring(6))]; // Let's see if we have one. // We have to decode name before we look it up // We could not use XmlToDataSet map as it contains // no hidden columns if (( c != null) && (c.ColumnMapping == MappingType.Hidden)) { // Got column and it is hidden ? foundColumns[c.Ordinal] = c.ConvertXmlToObject(dataReader.Value); } } } } } // Done with attributes // Now handle elements. This could be columns or nested tables. // /------------------- We are here after dealing with attributes // // Value // Value //
//
if ( dataReader.Read() && entryDepth < dataReader.Depth) { // Read to the next element and see if we're inside while ( entryDepth < dataReader.Depth ) { // Get out as soon as we've processed all nested nodes. switch (dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Element: // It's an element object o = nodeToSchemaMap.GetColumnSchema(table, dataReader, FIgnoreNamespace(dataReader)); // Get dataset element for this XML element c = o as DataColumn; // Perhaps, it's a column? if ( c != null ) { // Do we have matched column in this table? // Let's load column data if (foundColumns[c.Ordinal] == null) { // If this column was not found before LoadColumn (c, foundColumns); // Get column value } else { dataReader.Read(); // Advance to next element. } } else { DataTable nestedTable = o as DataTable; // Perhaps, it's a nested table ? if ( nestedTable != null ) { // Do we have matched nested table in DataSet ? LoadTable (nestedTable, true /* isNested */); // Yes. Load nested table (recursive) } // Not a table nor column? Check if it's schema. else if (ProcessXsdSchema()) { // Check for schema. Skip or load if found. continue; // Schema has been found. Process the next element // we're already at (done by schema processing). } else { // We've got element which is not supposed to he here according to the schema. // That might be a table which was misplaced. We should've thrown on that, // but we'll try to load it so we could keep compatibility. // We won't try to match to columns as we have no idea // which table this potential column might belong to. DataTable misplacedTable = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader)); // Try to get table for node if (misplacedTable != null) { // Got some matching table? LoadTable (misplacedTable, false /* isNested */); // While table's XML element is nested, // the table itself is not. Load it this way. } else { dataReader.Read(); // Not a table? Try next element. } } } break; case XmlNodeType.EntityReference: // Oops. No support for Entity Reference throw ExceptionBuilder.FoundEntity(); case XmlNodeType.Text: // It looks like a text. case XmlNodeType.Whitespace: // This actually could be case XmlNodeType.CDATA: // if we have XmlText in our table case XmlNodeType.SignificantWhitespace: textNodeValue = dataReader.ReadString(); // Get text node value. c = table.xmlText; // Get XML Text column from our table if (c != null && foundColumns[c.Ordinal] == null) { // If XmlText Column is set // and we do not have data already foundColumns[c.Ordinal] = c.ConvertXmlToObject(textNodeValue); // Read and store the data } break; default: dataReader.Read(); // We don't process that, skip to the next element. break; } } dataReader.Read(); // We're done here, proceed to the next element. } // It's the time to populate row with loaded data and add it to the table we'we just read to the table if (isDiffgram) { // In case of diffgram row = table.NewRow(table.NewUninitializedRecord()); // just create an empty row row.BeginEdit(); // and allow it's population with data for ( int i = foundColumns.Length - 1; i >= 0 ; --i) { // Check all columns c = collection[i]; // Get column for this index c[row.tempRecord] = null != foundColumns[i] ? foundColumns[i] : DBNull.Value; // Set column to loaded value of to // DBNull if value is missing. } row.EndEdit(); // Done with this row table.Rows.DiffInsertAt(row, rowOrder); // insert data to specific location // And do some diff processing if (hasChanges == null) { // No changes ? row.oldRecord = row.newRecord; // Restore old record } if ((hasChanges == Keywords.MODIFIED) || hasErrors) { table.RowDiffId[diffId] = row; } } else { for ( int i = foundColumns.Length -1; i >= 0 ; --i) { // Check all columns if (null == foundColumns[i]) { // Got data for this column ? c = collection[i]; // No. Get column for this index if (c.AllowDBNull && c.ColumnMapping != MappingType.Hidden && !c.AutoIncrement) { foundColumns[i] = DBNull.Value; // Assign DBNull if possible // table.Rows.Add() below will deal // with default values and autoincrement } } } row = table.Rows.AddWithColumnEvents(foundColumns); // Create, populate and add row } // Data is loaded into the row and row is added to the table at this point while (entryChild < childRowsStack.Count) { // Process child rows we might have DataRow childRow = (DataRow) childRowsStack.Pop(); // Get row from the stack bool unchanged = (childRow.RowState == DataRowState.Unchanged); // Is data the same as before? childRow.SetNestedParentRow(row, /*setNonNested*/ false); // Set parent row if (unchanged) // Restore record if child row's unchanged childRow.oldRecord = childRow.newRecord; } if (isNested) // Got parent ? childRowsStack.Push(row); // Push row to the stack } // Returns column value private void LoadColumn (DataColumn column, object[] foundColumns) { // /--------------------------------- We are here on entrance // / // Value // Value //
\------------------------------ We are here on exit //
// If we have something like this // FooVal We would grab first text-like node // Value In this case it would be "FooVal" // BarVal And not "Value" as you might think // This is how desktop works string text = String.Empty; // Column text. Assume empty string string xsiNilString = null; // Possible NIL attribute string int entryDepth = dataReader.Depth; // Store depth so we won't read too much if (dataReader.AttributeCount > 0) // If have attributes xsiNilString = dataReader.GetAttribute(Keywords.XSI_NIL, Keywords.XSINS); // Try to get NIL attribute // We have to do it before we move to the next element if (column.IsCustomType) { // Custom type column object columnValue = null; // Column value we're after. Assume no value. string xsiTypeString = null; // XSI type name from TYPE attribute string typeName = null; // Type name from MSD_INSTANCETYPE attribute XmlRootAttribute xmlAttrib = null; // Might need this attribute for XmlSerializer if (dataReader.AttributeCount > 0) { // If have attributes, get attributes we'll need xsiTypeString = dataReader.GetAttribute(Keywords.TYPE, Keywords.XSINS); typeName = dataReader.GetAttribute(Keywords.MSD_INSTANCETYPE, Keywords.MSDNS); } // Check if need to use XmlSerializer. We need to do that if type does not implement IXmlSerializable. // We also need to do that if no polymorphism for this type allowed. bool useXmlSerializer = !column.ImplementsIXMLSerializable && !( (column.DataType == typeof(Object)) || (typeName != null) || (xsiTypeString != null) ); // Check if we have an attribute telling us value is null. if ((xsiNilString != null) && XmlConvert.ToBoolean(xsiNilString)) { if (!useXmlSerializer) { // See if need to set typed null. if (typeName != null && typeName.Length > 0) { // Got type name columnValue = SqlUdtStorage.GetStaticNullForUdtType(Type.GetType(typeName)); } } if (null == columnValue) { // If no value, columnValue = DBNull.Value; // change to DBNull; } if ( !dataReader.IsEmptyElement ) // In case element is not empty while (dataReader.Read() && (entryDepth < dataReader.Depth)); // Read current elements dataReader.Read(); // And start reading next element. } else { // No NIL attribute. Get value bool skipped = false; if (column.Table.DataSet != null && column.Table.DataSet.UdtIsWrapped) { dataReader.Read(); // if UDT is wrapped, skip the wrapper skipped = true; } if (useXmlSerializer) { // Create an attribute for XmlSerializer if (skipped) { xmlAttrib = new XmlRootAttribute(dataReader.LocalName); xmlAttrib.Namespace = dataReader.NamespaceURI ; } else { xmlAttrib = new XmlRootAttribute(column.EncodedColumnName); xmlAttrib.Namespace = column.Namespace; } } columnValue = column.ConvertXmlToObject(dataReader, xmlAttrib); // Go get the value if (skipped) { dataReader.Read(); // if Wrapper is skipped, skip its end tag } } foundColumns[column.Ordinal] = columnValue; // Store value } else { // Not a custom type. if ( dataReader.Read() && entryDepth < dataReader.Depth) { // Read to the next element and see if we're inside. while (entryDepth < dataReader.Depth) { switch (dataReader.NodeType) { // Process nodes based on type case XmlNodeType.Text: // It looks like a text. And we need it. case XmlNodeType.Whitespace: case XmlNodeType.CDATA: case XmlNodeType.SignificantWhitespace: if (0 == text.Length) { // In case we do not have value already text = dataReader.Value; // Get value. // See if we have other text nodes near. In most cases this loop will not be executed. while ( dataReader.Read() && entryDepth < dataReader.Depth && IsTextLikeNode(dataReader.NodeType)) { text += dataReader.Value; // Concatenate other sequential text like // nodes we might have. This is rare. // We're using this instead of dataReader.ReadString() // which would do the same thing but slower. } } else { dataReader.ReadString(); // We've got column value already. Read this one and ignore it. } break; case XmlNodeType.Element: if (ProcessXsdSchema()) { // Check for schema. Skip or load if found. continue; // Schema has been found. Process the next element // we're already at (done by schema processing). } else { // We've got element which is not supposed to he here. // That might be table which was misplaced. // Or it might be a column inside column (also misplaced). object o = nodeToSchemaMap.GetColumnSchema(column.Table, dataReader, FIgnoreNamespace(dataReader)); // Get dataset element for this XML element DataColumn c = o as DataColumn; // Perhaps, it's a column? if ( c != null ) { // Do we have matched column in this table? // Let's load column data if (foundColumns[c.Ordinal] == null) { // If this column was not found before LoadColumn (c, foundColumns); // Get column value } else { dataReader.Read(); // Already loaded, proceed to the next element } } else { DataTable nestedTable = o as DataTable; // Perhaps, it's a nested table ? if ( nestedTable != null ) { // Do we have matched table in DataSet ? LoadTable (nestedTable, true /* isNested */); // Yes. Load nested table (recursive) } else { // Not a nested column nor nested table. // Let's try other tables in the DataSet DataTable misplacedTable = nodeToSchemaMap.GetTableForNode(dataReader, FIgnoreNamespace(dataReader)); // Try to get table for node if (misplacedTable != null) { // Got some table to match? LoadTable (misplacedTable, false /* isNested */); // While table's XML element is nested, // the table itself is not. Load it this way. } else { dataReader.Read(); // No match? Try next element } } } } break; case XmlNodeType.EntityReference: // Oops. No support for Entity Reference throw ExceptionBuilder.FoundEntity(); default: dataReader.Read(); // We don't process that, skip to the next element. break; } } dataReader.Read(); // We're done here. To the next element. } if (0 == text.Length && xsiNilString != null && XmlConvert.ToBoolean(xsiNilString)) { foundColumns[column.Ordinal] = DBNull.Value; // If no data and NIL attribute is true set value to null } else { foundColumns[column.Ordinal] = column.ConvertXmlToObject(text); } } } // Check for schema and skips or loads XSD schema if found. Returns true if schema found. // DataReader would be set on the first XML element after the schema of schema was found. // If no schema detected, reader's position will not change. private bool ProcessXsdSchema () { if (((object)dataReader.LocalName == XSD_SCHEMA && (object)dataReader.NamespaceURI == XSDNS )) { // Found XSD schema if ( ignoreSchema ) { // Should ignore it? dataReader.Skip(); // Yes, skip it } else { // Have to load schema. if ( isTableLevel ) { // Loading into the DataTable ? dataTable.ReadXSDSchema(dataReader, false); // Invoke ReadXSDSchema on a table nodeToSchemaMap = new XmlToDatasetMap(dataReader.NameTable, dataTable); } // Rebuild XML to DataSet map with new schema. else { // Loading into the DataSet ? dataSet.ReadXSDSchema(dataReader, false); // Invoke ReadXSDSchema on a DataSet nodeToSchemaMap = new XmlToDatasetMap(dataReader.NameTable, dataSet); } // Rebuild XML to DataSet map with new schema. } } else if (((object)dataReader.LocalName == XDR_SCHEMA && (object)dataReader.NamespaceURI == XDRNS ) || ((object)dataReader.LocalName == SQL_SYNC && (object)dataReader.NamespaceURI == UPDGNS)) { dataReader.Skip(); // Skip XDR or SQL sync } else { return false; // No schema found. That means reader's position // is unchganged. Report that to the caller. } return true; // Schema found, reader's position changed. } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK