|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Xml;
using System.Security;
using System.Collections;
using System.IO;
using System.Reflection.Emit;
/*
* Limitations:
* The ExceptionDataContract utilizes the ClassDataConract class in order to deserialize Exceptions with the ability to fill private fields.
* For the ClassDataContract to deserialize private fields, the name of the private field must exactly match the name of the field in xml.
* However the thick framework does not serialize exceptions using private field names. The thick framework is able to bypass using private field names because it has a mapping for each Exception
* made possible by implementing ISerializable. The mapping of every single .Net Exception Private Field to the Thick Framework serialized name is too much data for the ExceptionDataContract
* to hold.
* This class creates a mapping for the System.Exception private fields, which are considered the most important fields for an Exception.
* Therefore the only private fields that are supported for serialization/deserialization are those declared in System.Exception.
*
* For the serialization of classes derived from System.Exception all public properties are included into the xml out.
* There is no support for private properties other than those in System.Exception.
* In order for the property to be reset on deserialization it must implement a setter.
* Author: t-jicamp
*/
namespace System.Runtime.Serialization
{
internal sealed class ExceptionDataContract : DataContract
{
private XmlDictionaryString[] _contractNamespaces;
private XmlDictionaryString[] _memberNames;
private XmlDictionaryString[] _memberNamespaces;
[SecurityCritical]
private ExceptionDataContractCriticalHelper _helper;
[SecuritySafeCritical]
public ExceptionDataContract() : base(new ExceptionDataContractCriticalHelper())
{
_helper = base.Helper as ExceptionDataContractCriticalHelper;
_contractNamespaces = _helper.ContractNamespaces;
_memberNames = _helper.MemberNames;
_memberNamespaces = _helper.MemberNamespaces;
}
[SecuritySafeCritical]
public ExceptionDataContract(Type type) : base(new ExceptionDataContractCriticalHelper(type))
{
_helper = base.Helper as ExceptionDataContractCriticalHelper;
_contractNamespaces = _helper.ContractNamespaces;
_memberNames = _helper.MemberNames;
_memberNamespaces = _helper.MemberNamespaces;
}
public List<DataMember> Members
{
[SecuritySafeCritical]
get
{ return _helper.Members; }
}
internal override bool CanContainReferences //inherited as internal
{
get { return true; }
}
public static Dictionary<string, string> EssentialExceptionFields
{
[SecuritySafeCritical]
get
{ return ExceptionDataContractCriticalHelper.EssentialExceptionFields; }
}
public override Dictionary<XmlQualifiedName, DataContract> KnownDataContracts //inherited as internal
{
[SecuritySafeCritical]
get
{ return _helper.KnownDataContracts; }
[SecurityCritical]
set
{ _helper.KnownDataContracts = value; }
}
public XmlDictionaryString[] ContractNamespaces
{
get { return _contractNamespaces; }
set { _contractNamespaces = value; }
}
public XmlDictionaryString[] MemberNames
{
get { return _memberNames; }
set { _memberNames = value; }
}
public XmlDictionaryString[] MemberNamespaces
{
get { return _memberNamespaces; }
set { _memberNamespaces = value; }
}
[SecurityCritical]
private class ExceptionDataContractCriticalHelper : DataContract.DataContractCriticalHelper
{
private XmlDictionaryString[] _contractNamespaces;
private XmlDictionaryString[] _memberNames;
private XmlDictionaryString[] _memberNamespaces;
private ExceptionDataContract _baseContract;
private List<DataMember> _members;
private bool _hasDataContract;
private Dictionary<XmlQualifiedName, DataContract> _knownDataContracts;
private static readonly Dictionary<string, string> s_essentialExceptionFields; //Contains the essential fields to serialize an Exception. Not all fields are serialized in an Exception. Some private fields
//need to be serialized, but then again some need to be left out. This will keep track of which ones need to be serialized
//And also their display name for serialization which differs from their declared name.
/*
* The ordering of this dictionary is important due to the nature of the ClassDataContract that ExceptionDataContract depends on.
* In order for the ClassDataContract to correctly set the values for the members of the underlying class, it must have a list
* of members in the same order as the xml document it is reading. These members are created in the ImportDataMembers() method,
* and their ordering comes from this dictionary.
*
* This dictionary is in the order that the Full Framework declares System.Exceptions members. This order is established
* in the Full Framework version of System.Exception's Iserializable interface.
*/
static ExceptionDataContractCriticalHelper()
{
s_essentialExceptionFields = new Dictionary<string, string>();
s_essentialExceptionFields.Add("_className", "ClassName");
s_essentialExceptionFields.Add("_message", "Message");
s_essentialExceptionFields.Add("_data", "Data");
s_essentialExceptionFields.Add("_innerException", "InnerException");
s_essentialExceptionFields.Add("_helpURL", "HelpURL");
s_essentialExceptionFields.Add("_stackTraceString", "StackTraceString");
s_essentialExceptionFields.Add("_remoteStackTraceString", "RemoteStackTraceString");
s_essentialExceptionFields.Add("_remoteStackIndex", "RemoteStackIndex");
s_essentialExceptionFields.Add("_exceptionMethodString", "ExceptionMethod");
s_essentialExceptionFields.Add("_HResult", "HResult");
s_essentialExceptionFields.Add("_source", "Source");
s_essentialExceptionFields.Add("_watsonBuckets", "WatsonBuckets");
}
public ExceptionDataContractCriticalHelper()
{
IsValueType = false;
}
public ExceptionDataContractCriticalHelper(Type type)
: base(type)
{
this.StableName = DataContract.GetStableName(type, out _hasDataContract);
Type baseType = Globals.TypeOfException;
this.IsValueType = type.GetTypeInfo().IsValueType;
if (baseType != null && baseType != Globals.TypeOfObject && type != Globals.TypeOfException)
{
DataContract baseContract = DataContract.GetDataContract(baseType);
this.BaseContract = baseContract as ExceptionDataContract;
}
else
{
this.BaseContract = null;
}
ImportDataMembers();
ImportKnownTypes();
XmlDictionary dictionary = new XmlDictionary(2 + Members.Count);
Name = dictionary.Add(StableName.Name);
Namespace = dictionary.Add(StableName.Namespace);
int baseContractCount = 0;
if (BaseContract == null)
{
_memberNames = new XmlDictionaryString[Members.Count];
_memberNamespaces = new XmlDictionaryString[Members.Count];
_contractNamespaces = new XmlDictionaryString[1];
}
else
{
_memberNames = new XmlDictionaryString[Members.Count];
_memberNamespaces = new XmlDictionaryString[Members.Count];
baseContractCount = BaseContract._contractNamespaces.Length;
_contractNamespaces = new XmlDictionaryString[1 + baseContractCount];
Array.Copy(BaseContract._contractNamespaces, 0, _contractNamespaces, 0, baseContractCount);
}
_contractNamespaces[baseContractCount] = Namespace;
for (int i = 0; i < Members.Count; i++)
{
_memberNames[i] = dictionary.Add(Members[i].Name);
_memberNamespaces[i] = Namespace;
}
}
public ExceptionDataContract BaseContract
{
get { return _baseContract; }
set { _baseContract = value; }
}
public static Dictionary<string, string> EssentialExceptionFields
{
get { return s_essentialExceptionFields; }
}
internal override Dictionary<XmlQualifiedName, DataContract> KnownDataContracts // inherited as internal
{
[SecurityCritical]
get
{
if (_knownDataContracts == null)
_knownDataContracts = new Dictionary<XmlQualifiedName, DataContract>();
return _knownDataContracts;
}
[SecurityCritical]
set
{ /* do nothing */ }
}
public List<DataMember> Members
{
get { return _members; }
}
public XmlDictionaryString[] ContractNamespaces
{
get { return _contractNamespaces; }
set { _contractNamespaces = value; }
}
public XmlDictionaryString[] MemberNames
{
get { return _memberNames; }
set { _memberNames = value; }
}
public XmlDictionaryString[] MemberNamespaces
{
get { return _memberNamespaces; }
set { _memberNamespaces = value; }
}
private void ImportDataMembers()
{
/*
* DataMembers are used for the deserialization of Exceptions.
* DataMembers represent the fields and/or settable properties that the underlying Exception has.
* The DataMembers are stored and eventually passed to a ClassDataContract created from the underlying Exception.
* The ClassDataContract uses the list of DataMembers to set the fields/properties for the newly created Exception.
* If a DataMember is a property it must be settable.
*/
Type type = this.UnderlyingType;
if (type == Globals.TypeOfException)
{
ImportSystemExceptionDataMembers(); //System.Exception must be handled specially because it is the only exception that imports private fields.
return;
}
List<DataMember> tempMembers;
if (BaseContract != null)
{
tempMembers = new List<DataMember>(BaseContract.Members); //Don't set tempMembers = BaseContract.Members and then start adding, because this alters the base's reference.
}
else
{
tempMembers = new List<DataMember>();
}
Dictionary<string, DataMember> memberNamesTable = new Dictionary<string, DataMember>();
MemberInfo[] memberInfos;
memberInfos = type.GetMembers(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < memberInfos.Length; i++)
{
MemberInfo member = memberInfos[i];
//Public properties with set methods can be deserialized with the ClassDataContract
PropertyInfo publicProperty = member as PropertyInfo;
if (publicProperty != null && publicProperty.SetMethod != null)
{
DataMember memberContract = new DataMember(member);
memberContract.Name = DataContract.EncodeLocalName(member.Name);
memberContract.IsRequired = false;
memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType);
if (HasNoConflictWithBaseMembers(memberContract))
{
CheckAndAddMember(tempMembers, memberContract, memberNamesTable);
}
}
}
Interlocked.MemoryBarrier();
_members = tempMembers;
}
private void ImportSystemExceptionDataMembers()
{
/*
* The data members imported for System.Exception are private fields. They must be treated specially.
* The EssentialExceptionFields Dictionary keeps track of which private fields needs to be imported, and also the name that they should be serialized with.
*/
Type type = this.UnderlyingType;
List<DataMember> tempMembers;
tempMembers = new List<DataMember>();
Dictionary<string, DataMember> memberNamesTable = new Dictionary<string, DataMember>();
foreach (string fieldInfoName in EssentialExceptionFields.Keys)
{
FieldInfo member = type.GetField(fieldInfoName, BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Instance);
if (CanSerializeMember(member))
{
DataMember memberContract = new DataMember(member);
memberContract.Name = DataContract.EncodeLocalName(member.Name);
memberContract.IsRequired = false;
memberContract.IsNullable = DataContract.IsTypeNullable(memberContract.MemberType);
CheckAndAddMember(tempMembers, memberContract, memberNamesTable);
}
}
Interlocked.MemoryBarrier();
_members = tempMembers;
}
private void ImportKnownTypes()
{
DataContract dataDataContract = GetDataContract(typeof(IDictionary<object, object>));
this.KnownDataContracts.Add(dataDataContract.StableName, dataDataContract);
}
private bool HasNoConflictWithBaseMembers(DataMember memberContract)
{
//Don't add redundant members, this can happen if a property overrides it's base class implementation. Because the overriden property will appear as "declared" in that type.
foreach (DataMember dm in BaseContract.Members)
{
if (dm.Name.Equals(memberContract.Name))
{
return false;
}
}
return true;
}
}
private static bool CanSerializeMember(FieldInfo field)
{
return field != null &&
field.FieldType != Globals.TypeOfObject; // Don't really know how to serialize plain System.Object instance
}
private void WriteExceptionValue(XmlWriterDelegator writer, object value, XmlObjectSerializerWriteContext context)
{
/*
* Every private field present in System.Exception that is serialized in the thick framework is also serialized in this method.
* For classes that inherit from System.Exception all public properties are serialized.
* The reason that the Members property is not used here to get the properties to serialize is because Members contains only the properties with setters.
*/
Type type = value.GetType();
writer.WriteXmlnsAttribute("x", new XmlDictionary(1).Add(Globals.SchemaNamespace));
WriteSystemExceptionRequiredValues(writer, value, context);
PropertyInfo[] props = type.GetProperties();
foreach (PropertyInfo prop in props)
{
//Properties in System.Exception are handled in the call to WriteSystemExceptionRequiredValues, we don't want to repeat these properties.
if (PropertyIsSystemExceptionProperty(prop))
continue;
writer.WriteStartElement(prop.Name, "");
DataContract propDataContract = context.GetDataContract(prop.PropertyType);
if (prop.GetValue(value) != null && propDataContract != null && !TryCheckIfNoCountIDictionary(prop.PropertyType, prop.GetValue(value)))
{
if (!TryWritePrimitive(prop.PropertyType, prop.GetValue(value), writer, context))
{
writer.WriteAttributeString(Globals.XsiPrefix, "type", null, "a:" + propDataContract.StableName.Name);
writer.WriteXmlnsAttribute("a", new XmlDictionary(1).Add(propDataContract.StableName.Namespace));
context.SerializeWithoutXsiType(propDataContract, writer, prop.GetValue(value), prop.PropertyType.TypeHandle);
}
}
else
{
writer.WriteAttributeString(Globals.XsiPrefix, "nil", null, "true");
}
writer.WriteEndElement();
}
}
private void WriteSystemExceptionRequiredValues(XmlWriterDelegator writer, object value, XmlObjectSerializerWriteContext context)
{
Dictionary<string, object> exceptionFields = GetExceptionFieldValues((Exception)value);
object val;
foreach (string key in exceptionFields.Keys)
{
if (!exceptionFields.TryGetValue(key, out val))
continue;
Type fieldType;
FieldInfo FieldFind = Globals.TypeOfException.GetField(key, BindingFlags.Instance | BindingFlags.NonPublic);
if (FieldFind == null)
{
val = null; // need to nullify because the private fields that are necessary in Exception have changed.
fieldType = typeof(int); // can be any type, it doesn't matter. field type will be used to recover a contract, but the type won't be utilized.
}
else
fieldType = FieldFind.FieldType;
string fieldDisplayName;
if (EssentialExceptionFields.TryGetValue(key, out fieldDisplayName))
writer.WriteStartElement(fieldDisplayName, "");
else
writer.WriteStartElement(key, "");
DataContract fieldDataContract = context.GetDataContract(fieldType);
if (val != null && fieldDataContract != null && !TryCheckIfNoCountIDictionary(fieldType, val))
{
if (!TryWritePrimitive(fieldType, val, writer, context))
{
writer.WriteAttributeString(Globals.XsiPrefix, "type", null, "a:" + fieldDataContract.StableName.Name);
writer.WriteXmlnsAttribute("a", new XmlDictionary(1).Add(fieldDataContract.StableName.Namespace));
fieldDataContract.WriteXmlValue(writer, val, context);
}
}
else
{
writer.WriteAttributeString(Globals.XsiPrefix, "nil", null, "true");
}
writer.WriteEndElement();
}
}
private bool PropertyIsSystemExceptionProperty(PropertyInfo prop)
{
PropertyInfo[] props = Globals.TypeOfException.GetProperties();
foreach (PropertyInfo propInfo in props)
{
if (propInfo.Name.Equals(prop.Name))
{
return true;
}
}
return false;
}
private static void CheckAndAddMember(List<DataMember> members, DataMember memberContract, Dictionary<string, DataMember> memberNamesTable)
{
DataMember existingMemberContract;
if (memberNamesTable.TryGetValue(memberContract.Name, out existingMemberContract))
{
Type declaringType = memberContract.MemberInfo.DeclaringType;
DataContract.ThrowInvalidDataContractException(
string.Format(SRSerialization.DupMemberName,
existingMemberContract.MemberInfo.Name,
memberContract.MemberInfo.Name,
DataContract.GetClrTypeFullName(declaringType),
memberContract.Name),
declaringType);
}
memberNamesTable.Add(memberContract.Name, memberContract);
members.Add(memberContract);
}
[SecuritySafeCritical]
private Dictionary<string, object> GetExceptionFieldValues(Exception value)
{
// Obtain the unoverrided version of Message
Type exceptionType = Globals.TypeOfException;
PropertyInfo messageProperty = exceptionType.GetProperty("Message");
MethodInfo messageGetter = messageProperty.GetMethod;
#if !NET_NATIVE
DynamicMethod baseMessageImpl = new DynamicMethod("NonVirtual_Message", typeof(string), new Type[] { Globals.TypeOfException }, Globals.TypeOfException);
ILGenerator gen = baseMessageImpl.GetILGenerator();
gen.Emit(OpCodes.Ldarg, messageGetter.GetParameters().Length);
gen.EmitCall(OpCodes.Call, messageGetter, null);
gen.Emit(OpCodes.Ret);
string messageValue = (string)baseMessageImpl.Invoke(null, new object[] { value });
#else
string messageValue = string.Empty;
#endif
// Populate the values for the necessary System.Exception private fields.
Dictionary<string, object> fieldToValueDictionary = new Dictionary<string, object>();
fieldToValueDictionary.Add("_className", value.GetType().ToString());
fieldToValueDictionary.Add("_message", messageValue); //Thick framework retrieves the System.Exception implementation of message
fieldToValueDictionary.Add("_data", value.Data);
fieldToValueDictionary.Add("_innerException", value.InnerException);
fieldToValueDictionary.Add("_helpURL", value.HelpLink);
fieldToValueDictionary.Add("_stackTraceString", value.StackTrace);
fieldToValueDictionary.Add("_remoteStackTraceString", null);
fieldToValueDictionary.Add("_remoteStackIndex", 0);
fieldToValueDictionary.Add("_exceptionMethodString", null);
fieldToValueDictionary.Add("_HResult", value.HResult);
fieldToValueDictionary.Add("_source", null); //value.source caused transparency error on build.
fieldToValueDictionary.Add("_watsonBuckets", null);
return fieldToValueDictionary;
}
public override void WriteXmlValue(XmlWriterDelegator xmlWriter, object obj, XmlObjectSerializerWriteContext context)
{
Type type = obj.GetType();
if (!(typeof(Exception).IsAssignableFrom(type)))
{
throw new InvalidDataContractException("Cannot use ExceptionDataContract to serialize object with type: " + type);
}
WriteExceptionValue(xmlWriter, obj, context);
}
public override object ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
{
XmlReaderDelegator xmlDelegator = ParseReaderString(xmlReader);
ClassDataContract cdc = new ClassDataContract(this.UnderlyingType);
// The Class Data Contract created from the underlying exception type uses custom imported members that are
// created in this classes constructor. Here we clear out the Class Data Contract's default members and insert our own.
cdc.Members.Clear();
foreach (DataMember dm in this.Members)
{
cdc.Members.Add(dm);
}
cdc.MemberNames = _memberNames;
cdc.ContractNamespaces = _contractNamespaces;
cdc.MemberNamespaces = _memberNamespaces;
object obj = cdc.ReadXmlValue(xmlDelegator, context);
if (context != null)
context.AddNewObject(obj);
return obj;
}
private bool TryWritePrimitive(Type type, object value, XmlWriterDelegator writer, XmlObjectSerializerWriteContext context)
{
PrimitiveDataContract primitiveContract = PrimitiveDataContract.GetPrimitiveDataContract(type);
if (primitiveContract == null || primitiveContract.UnderlyingType == Globals.TypeOfObject)
return false;
writer.WriteAttributeString(Globals.XsiPrefix, "type", null, "x:" + primitiveContract.StableName.Name);
primitiveContract.WriteXmlValue(writer, value, context);
return true;
}
private static bool TryCheckIfNoCountIDictionary(Type type, object value)
{
if (value == null)
return true;
if (type == Globals.TypeOfIDictionary)
{
IDictionary tempDictImitation = (IDictionary)value;
return (tempDictImitation.Count == 0);
}
return false;
}
/*
* The reason this function exists is to provide compatibility for ExceptionDataContract to utilize ClassDataContract for deserialization.
* In order for ExceptionDataContract to deserialize using ClassDataContract the xml elements corresponding to private fields need
* to have their name replaced with the name of the private field they map to.
* Example: Message ---replaced to--> _message
*
* Currently this method utilizes a custom created class entitled ExceptionXmlParser that sits alongside the ExceptionDataContract
* The ExceptionXmlParser reads the xml string passed to it exchanges any element names that are presented in the name map
* in its constructor.
*
* The ExceptionXmlParser also gives each nested element the proper namespace for the given exception being deserialized.
* The ClassDataContract needs an exact match on the element name and the namespace in order to deserialize the value, because not all serialization
* explicitly inserts the xmlnamespace of nested objects, the exception xml parser handles this.
*/
private XmlReaderDelegator ParseReaderString(XmlReaderDelegator xmlReader)
{
//The reference to the xmlReader passed into this method should not be modified.
//The call to ReadOuterXml advances the xmlReader to the next object if the exception being parsed is a nested object of another class.
//When the call to ReadXmlValue that called this method returns, it is possible that the 'xmlReader' will still be used by the calling datacontract.
string EntryXmlString = xmlReader.UnderlyingReader.ReadOuterXml();
string result = ExceptionXmlParser.ParseExceptionXmlForClassDataContract(ReverseDictionary(EssentialExceptionFields), this.Namespace.ToString(), EntryXmlString);
byte[] byteBuffer = Encoding.UTF8.GetBytes(result);
XmlReaderDelegator xmlDelegator = new XmlReaderDelegator(XmlDictionaryReader.CreateTextReader(byteBuffer, XmlDictionaryReaderQuotas.Max));
xmlDelegator.MoveToContent();
return xmlDelegator;
}
private static Dictionary<string, string> ReverseDictionary(Dictionary<string, string> inDict)
{
Dictionary<string, string> mapDict = new Dictionary<string, string>();
foreach (string key in inDict.Keys)
{
string valString;
valString = inDict[key];
mapDict.Add(valString, key);
}
return mapDict;
}
}
/*
* This class is necessary to create a parsed xml document to utilize the ClassDataContract.
* The ExceptionXmlParser inserts necessary xmlns declarations for exception members, the namespace is necessary for ClassDataContract to identify
* the xml members as members of the Exception object.
* This ExceptionXmlParser also performs the transformation of the serialized names of exception private fields to the actual field names.
*/
internal sealed class ExceptionXmlParser : IDisposable
{
private XmlReader _reader;
private MemoryStream _ms;
private StreamWriter _sw;
private StringBuilder _sb;
private string _exceptionNamespace;
private Dictionary<string, string> _elementNamesToMap;
private bool _disposed;
private ExceptionXmlParser(Dictionary<string, string> dictMap, string exceptionNamespace)
{
// dictMap passes in the dictionary that is used for mapping field names to their serialized representations.
if (dictMap == null)
{
throw new ArgumentNullException("dictMap");
}
if (exceptionNamespace == null)
{
throw new ArgumentNullException("exceptionNamespace");
}
_elementNamesToMap = dictMap;
_exceptionNamespace = exceptionNamespace;
_sb = new StringBuilder();
_ms = new MemoryStream();
_sw = new StreamWriter(_ms);
_disposed = false;
}
public static string ParseExceptionXmlForClassDataContract(Dictionary<string, string> dictMap, string exceptionNamespace, string stringToParse)
{
using (ExceptionXmlParser parser = new ExceptionXmlParser(dictMap, exceptionNamespace))
{
return parser.ParseExceptionXml(stringToParse);
}
}
private string ParseExceptionXml(string stringToParse)
{
_sw.Write(stringToParse);
_sw.Flush();
_ms.Position = 0;
_reader = XmlReader.Create(_ms);
return ParseRootElement();
}
public void Dispose()
{
if (!_disposed)
{
_ms.Dispose();
_ms = null;
_sw.Dispose();
_sw = null;
_reader.Dispose();
_reader = null;
_disposed = true;
}
}
private string ParseRootElement()
{
_reader.MoveToContent();
string elementName = _reader.Name;
OpenRootElement(elementName);
if (_reader.IsEmptyElement)
{
_sb.Append("/>"); // close root element.
}
else
{
HandleRootElementsContents();
CloseRootElement(elementName);
}
return _sb.ToString();
}
private void ParseChildElement()
{
string elementName = _reader.Name;
elementName = OpenChildElement(elementName);
if (_reader.IsEmptyElement)
{
InlineCloser();
}
else
{
_sb.Append(">");
HandleChildElementContent();
CloseElement(elementName);
}
}
private string WriteElementNameWithBracketAndMapping(string name)
{
_sb.Append("<");
name = SwitchElementNameIfNecessary(name);
_sb.Append(name);
return name;
}
private string WriteExactElementNameWithBracket(string name)
{
_sb.AppendFormat("<{0}", name);
return name;
}
private string SwitchElementNameIfNecessary(string name)
{
string newName;
if (_elementNamesToMap.TryGetValue(name, out newName))
{
return newName;
}
return name;
}
private void InlineCloser()
{
_sb.Append("/>");
Read();
}
private void OpenRootElement(string elementName)
{
WriteElementNameWithBracketAndMapping(elementName);
for (int i = 0; i < _reader.AttributeCount; i++)
{
_reader.MoveToNextAttribute();
WriteAttribute(_reader.Name, _reader.Value);
}
_reader.MoveToElement();
}
private void CloseRootElement(string ElementName)
{
_sb.AppendFormat("</{0}>", ElementName);
}
private void HandleRootElementsContents()
{
_sb.Append(">");
Read();
while (_reader.NodeType == XmlNodeType.Element)
{
ParseChildElement();
}
}
private string OpenChildElement(string elementName)
{
elementName = WriteElementNameWithBracketAndMapping(elementName);
// the immediate child elements must have an xmlns attribute with namespace of the parent exception.
WriteAttribute("xmlns", _exceptionNamespace);
for (int i = 0; i < _reader.AttributeCount; i++)
{
_reader.MoveToNextAttribute();
if (_reader.Name.Equals("xmlns"))
continue;
WriteAttribute(_reader.Name, _reader.Value);
}
_reader.MoveToElement();
return elementName;
}
private void CloseElement(string ElementName)
{
_sb.AppendFormat("</{0}>", ElementName);
}
private void CopyXmlFromCurrentNode()
{
string elementName = _reader.Name;
// Start with the element
OpenExactElement(elementName);
// Handle an empty element
if (_reader.IsEmptyElement)
{
InlineCloser();
return;
}
_sb.Append(">");
// Append all children elements
Read();
while (_reader.NodeType == XmlNodeType.Element)
{
CopyXmlFromCurrentNode(); // Recursive call
}
if (_reader.NodeType == XmlNodeType.Text)
{
_sb.Append(_reader.Value);
Read();
}
// Finish with the element
CloseExactElement(elementName);
}
private void OpenExactElement(string elementName)
{
WriteExactElementNameWithBracket(elementName);
for (int i = 0; i < _reader.AttributeCount; i++)
{
_reader.MoveToNextAttribute();
WriteAttribute(_reader.Name, _reader.Value);
}
_reader.MoveToElement();
}
private void CloseExactElement(string elementName)
{
_sb.AppendFormat("</{0}>", elementName);
Read();
}
private void WriteAttribute(string name, string value)
{
_sb.AppendFormat(" {0}=\"{1}\"", name, value);
}
private void HandleChildElementContent()
{
Read();
if (_reader.NodeType == XmlNodeType.Text)
{
_sb.Append(_reader.Value);
Read();
_reader.ReadEndElement();
}
else
{
while (_reader.NodeType != XmlNodeType.EndElement)
{
CopyXmlFromCurrentNode();
}
_reader.ReadEndElement();
}
}
private void Read()
{
//There is no reason the reader should return false for properly serialized xml.
if (!_reader.Read())
{
throw new InvalidOperationException();
}
}
}
}
|