1301 lines
66 KiB
C#
1301 lines
66 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="XmlDataLoader.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
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(); //enzol 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)
|
||
|
// <Table> /----------------------------- We are here on entrance
|
||
|
// <Column>Value</Column>
|
||
|
// <AnotherColumn>Value</AnotherColumn>
|
||
|
// </Table> ...
|
||
|
// \------------------------------ 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 ) {
|
||
|
|
||
|
// <DataSet> /--------------------------- We are here on entrance
|
||
|
// <Table>
|
||
|
// <Column>Value</Column>
|
||
|
// <AnotherColumn>Value</AnotherColumn>
|
||
|
// </Table> /-------------------------- We are here on exit
|
||
|
// <AnotherTable>
|
||
|
// ...
|
||
|
// </AnotherTable>
|
||
|
// ...
|
||
|
// </DataSet>
|
||
|
|
||
|
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.
|
||
|
|
||
|
// <DataSet> /------------------- We are here after dealing with attributes
|
||
|
// <Table foo="FooValue" bar="BarValue">
|
||
|
// <Column>Value</Column>
|
||
|
// <AnotherColumn>Value</AnotherColumn>
|
||
|
// </Table>
|
||
|
// </DataSet>
|
||
|
|
||
|
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) {
|
||
|
|
||
|
// <DataSet> /--------------------------------- We are here on entrance
|
||
|
// <Table> /
|
||
|
// <Column>Value</Column>
|
||
|
// <AnotherColumn>Value</AnotherColumn>
|
||
|
// </Table> \------------------------------ We are here on exit
|
||
|
// </DataSet>
|
||
|
|
||
|
// <Column> If we have something like this
|
||
|
// <Foo>FooVal</Foo> We would grab first text-like node
|
||
|
// Value In this case it would be "FooVal"
|
||
|
// <Bar>BarVal</Bar> And not "Value" as you might think
|
||
|
// </Column> 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.
|
||
|
StringBuilder builder = null;
|
||
|
while ( dataReader.Read() && entryDepth < dataReader.Depth && IsTextLikeNode(dataReader.NodeType)) {
|
||
|
if (builder == null) {
|
||
|
builder = new StringBuilder(text);
|
||
|
}
|
||
|
builder.Append(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.
|
||
|
}
|
||
|
|
||
|
if (builder != null) {
|
||
|
text = builder.ToString();
|
||
|
}
|
||
|
}
|
||
|
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.
|
||
|
}
|
||
|
}
|
||
|
}
|