XmlDataLoader.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Data / System / Data / XmlDataLoader.cs / 1305376 / 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_[....]   && 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_[....];
        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_[....] = nameTable.Add(Keywords.SQL_[....]);
            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(DataStorage.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_[....] && (object)dataReader.NamespaceURI == UPDGNS)) { dataReader.Skip(); // Skip XDR or SQL [....] } 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. // // [....] // [....] //----------------------------------------------------------------------------- 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_[....] && 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_[....]; 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_[....] = nameTable.Add(Keywords.SQL_[....]); 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(DataStorage.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_[....] && (object)dataReader.NamespaceURI == UPDGNS)) { dataReader.Skip(); // Skip XDR or SQL [....] } 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.

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