|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace System.Xml.Serialization
{
///<internalonly/>
public abstract class XmlSerializationWriter : XmlSerializationGeneratedCode
{
private XmlWriter _w = null!;
private XmlSerializerNamespaces? _namespaces;
private int _tempNamespacePrefix;
private HashSet<int>? _usedPrefixes;
private Hashtable? _references;
private string? _idBase;
private int _nextId;
private Hashtable? _typeEntries;
private ArrayList? _referencesToWrite;
private Hashtable? _objectsInUse;
private readonly string _aliasBase = "q";
private bool _soap12;
private bool _escapeName = true;
//char buffer for serializing primitive values
private readonly char[] _primitivesBuffer = new char[64];
// this method must be called before any generated serialization methods are called
internal void Init(XmlWriter w, XmlSerializerNamespaces? namespaces, string? encodingStyle, string? idBase)
{
_w = w;
_namespaces = namespaces;
_soap12 = (encodingStyle == Soap12.Encoding);
_idBase = idBase;
}
protected bool EscapeName
{
get
{
return _escapeName;
}
set
{
_escapeName = value;
}
}
protected XmlWriter Writer
{
get
{
return _w;
}
set
{
_w = value;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected ArrayList? Namespaces
{
get
{
return _namespaces?.NamespaceList;
}
[UnconditionalSuppressMessage("AotAnalysis", "IL3050", Justification = "ToArray is called for known reference types only.")]
set
{
if (value == null)
{
_namespaces = null;
}
else
{
XmlQualifiedName[] qnames = (XmlQualifiedName[])value.ToArray(typeof(XmlQualifiedName));
_namespaces = new XmlSerializerNamespaces(qnames);
}
}
}
protected static byte[] FromByteArrayBase64(byte[] value)
{
// Unlike other "From" functions that one is just a place holder for automatic code generation.
// The reason is performance and memory consumption for (potentially) big 64base-encoded chunks
// And it is assumed that the caller generates the code that will distinguish between byte[] and string return types
//
return value;
}
///<internalonly/>
protected static Assembly? ResolveDynamicAssembly(string assemblyFullName)
{
return DynamicAssemblies.Get(assemblyFullName);
}
[return: NotNullIfNotNull(nameof(value))]
protected static string? FromByteArrayHex(byte[]? value)
{
return XmlCustomFormatter.FromByteArrayHex(value);
}
protected static string FromDateTime(DateTime value)
{
return XmlCustomFormatter.FromDateTime(value);
}
internal static bool TryFormatDateTime(DateTime value, Span<char> destination, out int charsWritten)
{
return XmlCustomFormatter.TryFormatDateTime(value, destination, out charsWritten);
}
protected static string FromDate(DateTime value)
{
return XmlCustomFormatter.FromDate(value);
}
protected static string FromTime(DateTime value)
{
return XmlCustomFormatter.FromTime(value);
}
protected static string FromChar(char value)
{
return XmlCustomFormatter.FromChar(value);
}
protected static string FromEnum(long value, string[] values, long[] ids)
{
return XmlCustomFormatter.FromEnum(value, values, ids, null);
}
protected static string FromEnum(long value, string[] values, long[] ids, string typeName)
{
return XmlCustomFormatter.FromEnum(value, values, ids, typeName);
}
[return: NotNullIfNotNull(nameof(name))]
protected static string? FromXmlName(string? name)
{
return XmlCustomFormatter.FromXmlName(name);
}
[return: NotNullIfNotNull(nameof(ncName))]
protected static string? FromXmlNCName(string? ncName)
{
return XmlCustomFormatter.FromXmlNCName(ncName);
}
[return: NotNullIfNotNull(nameof(nmToken))]
protected static string? FromXmlNmToken(string? nmToken)
{
return XmlCustomFormatter.FromXmlNmToken(nmToken);
}
[return: NotNullIfNotNull(nameof(nmTokens))]
protected static string? FromXmlNmTokens(string? nmTokens)
{
return XmlCustomFormatter.FromXmlNmTokens(nmTokens);
}
protected void WriteXsiType(string name, string? ns)
{
WriteAttribute("type", XmlSchema.InstanceNamespace, GetQualifiedName(name, ns));
}
[RequiresUnreferencedCode("calls GetPrimitiveTypeName")]
private XmlQualifiedName GetPrimitiveTypeName(Type type)
{
return GetPrimitiveTypeName(type, true)!;
}
[RequiresUnreferencedCode("calls CreateUnknownTypeException")]
private XmlQualifiedName? GetPrimitiveTypeName(Type type, bool throwIfUnknown)
{
XmlQualifiedName? qname = GetPrimitiveTypeNameInternal(type);
if (throwIfUnknown && qname == null)
throw CreateUnknownTypeException(type);
return qname;
}
internal static XmlQualifiedName? GetPrimitiveTypeNameInternal(Type type)
{
string typeName;
string typeNs = XmlSchema.Namespace;
switch (Type.GetTypeCode(type))
{
case TypeCode.String: typeName = "string"; break;
case TypeCode.Int32: typeName = "int"; break;
case TypeCode.Boolean: typeName = "boolean"; break;
case TypeCode.Int16: typeName = "short"; break;
case TypeCode.Int64: typeName = "long"; break;
case TypeCode.Single: typeName = "float"; break;
case TypeCode.Double: typeName = "double"; break;
case TypeCode.Decimal: typeName = "decimal"; break;
case TypeCode.DateTime: typeName = "dateTime"; break;
case TypeCode.Byte: typeName = "unsignedByte"; break;
case TypeCode.SByte: typeName = "byte"; break;
case TypeCode.UInt16: typeName = "unsignedShort"; break;
case TypeCode.UInt32: typeName = "unsignedInt"; break;
case TypeCode.UInt64: typeName = "unsignedLong"; break;
case TypeCode.Char:
typeName = "char";
typeNs = UrtTypes.Namespace;
break;
default:
if (type == typeof(XmlQualifiedName)) typeName = "QName";
else if (type == typeof(byte[])) typeName = "base64Binary";
else if (type == typeof(Guid))
{
typeName = "guid";
typeNs = UrtTypes.Namespace;
}
else if (type == typeof(TimeSpan))
{
typeName = "TimeSpan";
typeNs = UrtTypes.Namespace;
}
else if (type == typeof(DateTimeOffset))
{
typeName = "dateTimeOffset";
typeNs = UrtTypes.Namespace;
}
else if (type == typeof(XmlNode[]))
{
typeName = Soap.UrType;
}
else
return null;
break;
}
return new XmlQualifiedName(typeName, typeNs);
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WriteTypedPrimitive(string? name, string? ns, object o, bool xsiType)
{
string? value = null;
string type;
string typeNs = XmlSchema.Namespace;
bool writeRaw = true;
bool writeDirect = false;
Type t = o.GetType();
bool wroteStartElement = false;
bool? tryFormatResult = null;
int charsWritten = -1;
switch (Type.GetTypeCode(t))
{
case TypeCode.String:
value = (string)o;
type = "string";
writeRaw = false;
break;
case TypeCode.Int32:
tryFormatResult = XmlConvert.TryFormat((int)o, _primitivesBuffer, out charsWritten);
type = "int";
break;
case TypeCode.Boolean:
tryFormatResult = XmlConvert.TryFormat((bool)o, _primitivesBuffer, out charsWritten);
type = "boolean";
break;
case TypeCode.Int16:
tryFormatResult = XmlConvert.TryFormat((short)o, _primitivesBuffer, out charsWritten);
type = "short";
break;
case TypeCode.Int64:
tryFormatResult = XmlConvert.TryFormat((long)o, _primitivesBuffer, out charsWritten);
type = "long";
break;
case TypeCode.Single:
tryFormatResult = XmlConvert.TryFormat((float)o, _primitivesBuffer, out charsWritten);
type = "float";
break;
case TypeCode.Double:
tryFormatResult = XmlConvert.TryFormat((double)o, _primitivesBuffer, out charsWritten);
type = "double";
break;
case TypeCode.Decimal:
tryFormatResult = XmlConvert.TryFormat((decimal)o, _primitivesBuffer, out charsWritten);
type = "decimal";
break;
case TypeCode.DateTime:
tryFormatResult = TryFormatDateTime((DateTime)o, _primitivesBuffer, out charsWritten);
type = "dateTime";
break;
case TypeCode.Char:
tryFormatResult = XmlConvert.TryFormat((ushort)(char)o, _primitivesBuffer, out charsWritten);
type = "char";
typeNs = UrtTypes.Namespace;
break;
case TypeCode.Byte:
tryFormatResult = XmlConvert.TryFormat((byte)o, _primitivesBuffer, out charsWritten);
type = "unsignedByte";
break;
case TypeCode.SByte:
tryFormatResult = XmlConvert.TryFormat((sbyte)o, _primitivesBuffer, out charsWritten);
type = "byte";
break;
case TypeCode.UInt16:
tryFormatResult = XmlConvert.TryFormat((ushort)o, _primitivesBuffer, out charsWritten);
type = "unsignedShort";
break;
case TypeCode.UInt32:
tryFormatResult = XmlConvert.TryFormat((uint)o, _primitivesBuffer, out charsWritten);
type = "unsignedInt";
break;
case TypeCode.UInt64:
tryFormatResult = XmlConvert.TryFormat((ulong)o, _primitivesBuffer, out charsWritten);
type = "unsignedLong";
break;
default:
if (t == typeof(XmlQualifiedName))
{
type = "QName";
// need to write start element ahead of time to establish context
// for ns definitions by FromXmlQualifiedName
wroteStartElement = true;
if (name == null)
_w.WriteStartElement(type, typeNs);
else
_w.WriteStartElement(name, ns);
value = FromXmlQualifiedName((XmlQualifiedName)o, false);
}
else if (t == typeof(byte[]))
{
value = string.Empty;
writeDirect = true;
type = "base64Binary";
}
else if (t == typeof(Guid))
{
tryFormatResult = XmlConvert.TryFormat((Guid)o, _primitivesBuffer, out charsWritten);
type = "guid";
typeNs = UrtTypes.Namespace;
}
else if (t == typeof(TimeSpan))
{
tryFormatResult = XmlConvert.TryFormat((TimeSpan)o, _primitivesBuffer, out charsWritten);
type = "TimeSpan";
typeNs = UrtTypes.Namespace;
}
else if (t == typeof(DateTimeOffset))
{
tryFormatResult = XmlConvert.TryFormat((DateTimeOffset)o, _primitivesBuffer, out charsWritten);
type = "dateTimeOffset";
typeNs = UrtTypes.Namespace;
}
else if (typeof(XmlNode[]).IsAssignableFrom(t))
{
if (name == null)
_w.WriteStartElement(Soap.UrType, XmlSchema.Namespace);
else
_w.WriteStartElement(name, ns);
XmlNode[] xmlNodes = (XmlNode[])o;
for (int i = 0; i < xmlNodes.Length; i++)
{
if (xmlNodes[i] == null)
continue;
xmlNodes[i].WriteTo(_w);
}
_w.WriteEndElement();
return;
}
else
{
throw CreateUnknownTypeException(t);
}
break;
}
if (!wroteStartElement)
{
if (name == null)
_w.WriteStartElement(type, typeNs);
else
_w.WriteStartElement(name, ns);
}
if (xsiType) WriteXsiType(type, typeNs);
if (writeDirect)
{
// only one type currently writes directly to XML stream
XmlCustomFormatter.WriteArrayBase64(_w, (byte[])o, 0, ((byte[])o).Length);
}
else if (tryFormatResult != null)
{
Debug.Assert(tryFormatResult.Value, "Something goes wrong with formatting primitives to the buffer.");
#if DEBUG
const string escapeChars = "<>\"'&";
ReadOnlySpan<char> span = _primitivesBuffer;
Debug.Assert(!span.Slice(0, charsWritten).ContainsAny(escapeChars), "Primitive value contains illegal xml char.");
#endif
//all the primitive types except string and XmlQualifiedName writes to the buffer
_w.WriteRaw(_primitivesBuffer, 0, charsWritten);
}
else
{
if (value == null)
_w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true");
else if (writeRaw)
{
_w.WriteRaw(value);
}
else
_w.WriteString(value);
}
_w.WriteEndElement();
}
private string GetQualifiedName(string name, string? ns)
{
if (string.IsNullOrEmpty(ns)) return name;
string? prefix = _w.LookupPrefix(ns);
if (prefix == null)
{
if (ns == XmlReservedNs.NsXml)
{
prefix = "xml";
}
else
{
prefix = NextPrefix();
WriteAttribute("xmlns", prefix, null, ns);
}
}
else if (prefix.Length == 0)
{
return name;
}
return $"{prefix}:{name}";
}
protected string? FromXmlQualifiedName(XmlQualifiedName? xmlQualifiedName)
{
return FromXmlQualifiedName(xmlQualifiedName, true);
}
protected string? FromXmlQualifiedName(XmlQualifiedName? xmlQualifiedName, bool ignoreEmpty)
{
if (xmlQualifiedName == null) return null;
if (xmlQualifiedName.IsEmpty && ignoreEmpty) return null;
return GetQualifiedName(EscapeName ? XmlConvert.EncodeLocalName(xmlQualifiedName.Name) : xmlQualifiedName.Name, xmlQualifiedName.Namespace);
}
protected void WriteStartElement(string name)
{
WriteStartElement(name, null, null, false, null);
}
protected void WriteStartElement(string name, string? ns)
{
WriteStartElement(name, ns, null, false, null);
}
protected void WriteStartElement(string name, string? ns, bool writePrefixed)
{
WriteStartElement(name, ns, null, writePrefixed, null);
}
protected void WriteStartElement(string name, string? ns, object? o)
{
WriteStartElement(name, ns, o, false, null);
}
protected void WriteStartElement(string name, string? ns, object? o, bool writePrefixed)
{
WriteStartElement(name, ns, o, writePrefixed, null);
}
protected void WriteStartElement(string name, string? ns, object? o, bool writePrefixed, XmlSerializerNamespaces? xmlns)
{
if (o != null && _objectsInUse != null)
{
if (_objectsInUse.ContainsKey(o)) throw new InvalidOperationException(SR.Format(SR.XmlCircularReference, o.GetType().FullName));
_objectsInUse.Add(o, o);
}
string? prefix = null;
bool needEmptyDefaultNamespace = false;
if (_namespaces != null)
{
_namespaces.TryLookupPrefix(ns, out prefix);
if (_namespaces.TryLookupNamespace("", out string? defaultNS))
{
if (string.IsNullOrEmpty(defaultNS))
needEmptyDefaultNamespace = true;
if (ns != defaultNS)
writePrefixed = true;
}
_usedPrefixes = ListUsedPrefixes(_namespaces, _aliasBase);
}
if (writePrefixed && prefix == null && ns != null && ns.Length > 0)
{
prefix = _w.LookupPrefix(ns);
if (string.IsNullOrEmpty(prefix))
{
prefix = NextPrefix();
}
}
if (prefix == null && xmlns != null)
{
xmlns.TryLookupPrefix(ns, out prefix);
}
if (needEmptyDefaultNamespace && prefix == null && !string.IsNullOrEmpty(ns))
prefix = NextPrefix();
_w.WriteStartElement(prefix, name, ns);
if (_namespaces != null)
{
foreach (XmlQualifiedName qname in _namespaces.Namespaces)
{
string alias = qname.Name;
string? aliasNs = qname.Namespace;
if (alias.Length == 0 && string.IsNullOrEmpty(aliasNs))
continue;
if (string.IsNullOrEmpty(aliasNs))
{
if (alias.Length > 0)
throw new InvalidOperationException(SR.Format(SR.XmlInvalidXmlns, alias));
WriteAttribute(nameof(xmlns), alias, null, aliasNs);
}
else
{
if (_w.LookupPrefix(aliasNs) == null)
{
// write the default namespace declaration only if we have not written it already, over wise we just ignore one provided by the user
if (prefix == null && alias.Length == 0)
break;
WriteAttribute(nameof(xmlns), alias, null, aliasNs);
}
}
}
}
WriteNamespaceDeclarations(xmlns);
}
private static HashSet<int>? ListUsedPrefixes(XmlSerializerNamespaces nsList, string prefix)
{
var qnIndexes = new HashSet<int>();
int prefixLength = prefix.Length;
const string MaxInt32 = "2147483647";
foreach (XmlQualifiedName qname in nsList.Namespaces)
{
if (qname.Name.Length > prefixLength)
{
string name = qname.Name;
if (name.Length > prefixLength && name.Length <= prefixLength + MaxInt32.Length && name.StartsWith(prefix, StringComparison.Ordinal))
{
bool numeric = true;
for (int j = prefixLength; j < name.Length; j++)
{
if (!char.IsDigit(name, j))
{
numeric = false;
break;
}
}
if (numeric)
{
long index = long.Parse(name.AsSpan(prefixLength), NumberStyles.Integer, CultureInfo.InvariantCulture);
if (index <= int.MaxValue)
{
int newIndex = (int)index;
qnIndexes.Add(newIndex);
}
}
}
}
}
if (qnIndexes.Count > 0)
{
return qnIndexes;
}
return null;
}
protected void WriteNullTagEncoded(string? name)
{
WriteNullTagEncoded(name, null);
}
protected void WriteNullTagEncoded(string? name, string? ns)
{
if (string.IsNullOrEmpty(name))
return;
WriteStartElement(name, ns, null, true);
_w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true");
_w.WriteEndElement();
}
protected void WriteNullTagLiteral(string? name)
{
WriteNullTagLiteral(name, null);
}
protected void WriteNullTagLiteral(string? name, string? ns)
{
if (string.IsNullOrEmpty(name))
return;
WriteStartElement(name, ns, null, false);
_w.WriteAttributeString("nil", XmlSchema.InstanceNamespace, "true");
_w.WriteEndElement();
}
protected void WriteEmptyTag(string? name)
{
WriteEmptyTag(name, null);
}
protected void WriteEmptyTag(string? name, string? ns)
{
if (string.IsNullOrEmpty(name))
return;
WriteStartElement(name, ns, null, false);
_w.WriteEndElement();
}
protected void WriteEndElement()
{
_w.WriteEndElement();
}
protected void WriteEndElement(object? o)
{
_w.WriteEndElement();
if (o != null && _objectsInUse != null)
{
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (!_objectsInUse.ContainsKey(o)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "missing stack object of type " + o.GetType().FullName));
#endif
_objectsInUse.Remove(o);
}
}
protected void WriteSerializable(IXmlSerializable? serializable, string name, string ns, bool isNullable)
{
WriteSerializable(serializable, name, ns, isNullable, true);
}
protected void WriteSerializable(IXmlSerializable? serializable, string name, string? ns, bool isNullable, bool wrapped)
{
if (serializable == null)
{
if (isNullable) WriteNullTagLiteral(name, ns);
return;
}
if (wrapped)
{
_w.WriteStartElement(name, ns);
}
serializable.WriteXml(_w);
if (wrapped)
{
_w.WriteEndElement();
}
}
protected void WriteNullableStringEncoded(string name, string? ns, string? value, XmlQualifiedName? xsiType)
{
if (value == null)
WriteNullTagEncoded(name, ns);
else
WriteElementString(name, ns, value, xsiType);
}
protected void WriteNullableStringLiteral(string name, string? ns, string? value)
{
if (value == null)
WriteNullTagLiteral(name, ns);
else
WriteElementString(name, ns, value, null);
}
protected void WriteNullableStringEncodedRaw(string name, string? ns, string? value, XmlQualifiedName? xsiType)
{
if (value == null)
WriteNullTagEncoded(name, ns);
else
WriteElementStringRaw(name, ns, value, xsiType);
}
protected void WriteNullableStringEncodedRaw(string name, string? ns, byte[]? value, XmlQualifiedName? xsiType)
{
if (value == null)
WriteNullTagEncoded(name, ns);
else
WriteElementStringRaw(name, ns, value, xsiType);
}
protected void WriteNullableStringLiteralRaw(string name, string? ns, string? value)
{
if (value == null)
WriteNullTagLiteral(name, ns);
else
WriteElementStringRaw(name, ns, value, null);
}
protected void WriteNullableStringLiteralRaw(string name, string? ns, byte[]? value)
{
if (value == null)
WriteNullTagLiteral(name, ns);
else
WriteElementStringRaw(name, ns, value, null);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected void WriteNullableQualifiedNameEncoded(string name, string? ns, XmlQualifiedName? value, XmlQualifiedName? xsiType)
{
if (value == null)
WriteNullTagEncoded(name, ns);
else
WriteElementQualifiedName(name, ns, value, xsiType);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected void WriteNullableQualifiedNameLiteral(string name, string? ns, XmlQualifiedName? value)
{
if (value == null)
WriteNullTagLiteral(name, ns);
else
WriteElementQualifiedName(name, ns, value, null);
}
protected void WriteElementEncoded(XmlNode? node, string name, string? ns, bool isNullable, bool any)
{
if (node == null)
{
if (isNullable) WriteNullTagEncoded(name, ns);
return;
}
WriteElement(node, name, ns, isNullable, any);
}
protected void WriteElementLiteral(XmlNode? node, string name, string? ns, bool isNullable, bool any)
{
if (node == null)
{
if (isNullable) WriteNullTagLiteral(name, ns);
return;
}
WriteElement(node, name, ns, isNullable, any);
}
private void WriteElement(XmlNode node, string name, string? ns, bool isNullable, bool any)
{
if (typeof(XmlAttribute).IsAssignableFrom(node.GetType()))
throw new InvalidOperationException(SR.XmlNoAttributeHere);
if (node is XmlDocument)
{
node = ((XmlDocument)node).DocumentElement!;
if (node == null)
{
if (isNullable) WriteNullTagEncoded(name, ns);
return;
}
}
if (any)
{
if (node is XmlElement && name != null && name.Length > 0)
{
// need to check against schema
if (node.LocalName != name || node.NamespaceURI != ns)
throw new InvalidOperationException(SR.Format(SR.XmlElementNameMismatch, node.LocalName, node.NamespaceURI, name, ns));
}
}
else
_w.WriteStartElement(name, ns);
node.WriteTo(_w);
if (!any)
_w.WriteEndElement();
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected Exception CreateUnknownTypeException(object o)
{
return CreateUnknownTypeException(o.GetType());
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected Exception CreateUnknownTypeException(Type type)
{
if (typeof(IXmlSerializable).IsAssignableFrom(type)) return new InvalidOperationException(SR.Format(SR.XmlInvalidSerializable, type.FullName));
TypeDesc typeDesc = new TypeScope().GetTypeDesc(type);
if (!typeDesc.IsStructLike) return new InvalidOperationException(SR.Format(SR.XmlInvalidUseOfType, type.FullName));
return new InvalidOperationException(SR.Format(SR.XmlUnxpectedType, type.FullName));
}
protected Exception CreateMismatchChoiceException(string value, string elementName, string enumValue)
{
// Value of {0} mismatches the type of {1}, you need to set it to {2}.
return new InvalidOperationException(SR.Format(SR.XmlChoiceMismatchChoiceException, elementName, value, enumValue));
}
protected Exception CreateUnknownAnyElementException(string name, string ns)
{
return new InvalidOperationException(SR.Format(SR.XmlUnknownAnyElement, name, ns));
}
protected Exception CreateInvalidChoiceIdentifierValueException(string type, string identifier)
{
return new InvalidOperationException(SR.Format(SR.XmlInvalidChoiceIdentifierValue, type, identifier));
}
protected Exception CreateChoiceIdentifierValueException(string value, string identifier, string name, string ns)
{
// XmlChoiceIdentifierMismatch=Value '{0}' of the choice identifier '{1}' does not match element '{2}' from namespace '{3}'.
return new InvalidOperationException(SR.Format(SR.XmlChoiceIdentifierMismatch, value, identifier, name, ns));
}
protected Exception CreateInvalidEnumValueException(object value, string typeName)
{
return new InvalidOperationException(SR.Format(SR.XmlUnknownConstant, value, typeName));
}
protected Exception CreateInvalidAnyTypeException(object o)
{
return CreateInvalidAnyTypeException(o.GetType());
}
protected Exception CreateInvalidAnyTypeException(Type type)
{
return new InvalidOperationException(SR.Format(SR.XmlIllegalAnyElement, type.FullName));
}
protected void WriteReferencingElement(string n, string? ns, object? o)
{
WriteReferencingElement(n, ns, o, false);
}
protected void WriteReferencingElement(string n, string? ns, object? o, bool isNullable)
{
if (o == null)
{
if (isNullable) WriteNullTagEncoded(n, ns);
return;
}
WriteStartElement(n, ns, null, true);
if (_soap12)
_w.WriteAttributeString("ref", Soap12.Encoding, GetId(o, true));
else
_w.WriteAttributeString("href", $"#{GetId(o, true)}");
_w.WriteEndElement();
}
private bool IsIdDefined(object o)
{
if (_references != null) return _references.Contains(o);
else return false;
}
private string GetId(object o, bool addToReferencesList)
{
if (_references == null)
{
_references = new Hashtable();
_referencesToWrite = new ArrayList();
}
string? id = (string?)_references[o];
if (id == null)
{
id = string.Create(CultureInfo.InvariantCulture, $"{_idBase}id{++_nextId}");
_references.Add(o, id);
if (addToReferencesList) _referencesToWrite!.Add(o);
}
return id;
}
protected void WriteId(object o)
{
WriteId(o, true);
}
private void WriteId(object o, bool addToReferencesList)
{
if (_soap12)
_w.WriteAttributeString("id", Soap12.Encoding, GetId(o, addToReferencesList));
else
_w.WriteAttributeString("id", GetId(o, addToReferencesList));
}
protected void WriteXmlAttribute(XmlNode node)
{
WriteXmlAttribute(node, null);
}
protected void WriteXmlAttribute(XmlNode node, object? container)
{
XmlAttribute? attr = node as XmlAttribute;
if (attr == null) throw new InvalidOperationException(SR.XmlNeedAttributeHere);
if (attr.Value != null)
{
if (attr.NamespaceURI == Wsdl.Namespace && attr.LocalName == Wsdl.ArrayType)
{
string dims;
XmlQualifiedName qname = TypeScope.ParseWsdlArrayType(attr.Value, out dims, (container is XmlSchemaObject) ? (XmlSchemaObject)container : null);
string? value = FromXmlQualifiedName(qname, true) + dims;
//<xsd:attribute xmlns:q3="s0" wsdl:arrayType="q3:FoosBase[]" xmlns:q4="http://schemas.xmlsoap.org/soap/encoding/" ref="q4:arrayType" />
WriteAttribute(Wsdl.ArrayType, Wsdl.Namespace, value);
}
else
{
WriteAttribute(attr.Name, attr.NamespaceURI, attr.Value);
}
}
}
protected void WriteAttribute(string localName, string? ns, string? value)
{
if (value == null) return;
if (localName != "xmlns" && !localName.StartsWith("xmlns:", StringComparison.Ordinal))
{
int colon = localName.IndexOf(':');
if (colon < 0)
{
if (ns == XmlReservedNs.NsXml)
{
string? prefix = _w.LookupPrefix(ns);
if (string.IsNullOrEmpty(prefix))
{
prefix = "xml";
}
_w.WriteAttributeString(prefix, localName, ns, value);
}
else
{
_w.WriteAttributeString(localName, ns, value);
}
}
else
{
string prefix = localName.Substring(0, colon);
_w.WriteAttributeString(prefix, localName.Substring(colon + 1), ns, value);
}
}
}
protected void WriteAttribute(string localName, string ns, byte[]? value)
{
if (value == null) return;
if (localName != "xmlns" && !localName.StartsWith("xmlns:", StringComparison.Ordinal))
{
int colon = localName.IndexOf(':');
if (colon < 0)
{
if (ns == XmlReservedNs.NsXml)
{
_w.WriteStartAttribute("xml", localName, ns);
}
else
{
_w.WriteStartAttribute(null, localName, ns);
}
}
else
{
string? prefix = _w.LookupPrefix(ns);
_w.WriteStartAttribute(prefix, localName.Substring(colon + 1), ns);
}
XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length);
_w.WriteEndAttribute();
}
}
protected void WriteAttribute(string localName, string? value)
{
if (value == null) return;
_w.WriteAttributeString(localName, null, value);
}
protected void WriteAttribute(string localName, byte[]? value)
{
if (value == null) return;
_w.WriteStartAttribute(null, localName, (string?)null);
XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length);
_w.WriteEndAttribute();
}
protected void WriteAttribute(string? prefix, string localName, string? ns, string? value)
{
if (value == null) return;
_w.WriteAttributeString(prefix, localName, null, value);
}
protected void WriteValue(string? value)
{
if (value == null) return;
_w.WriteString(value);
}
protected void WriteValue(byte[]? value)
{
if (value == null) return;
XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length);
}
protected void WriteStartDocument()
{
if (_w.WriteState == WriteState.Start)
{
_w.WriteStartDocument();
}
}
protected void WriteElementString(string localName, string? value)
{
WriteElementString(localName, null, value, null);
}
protected void WriteElementString(string localName, string? ns, string? value)
{
WriteElementString(localName, ns, value, null);
}
protected void WriteElementString(string localName, string? value, XmlQualifiedName? xsiType)
{
WriteElementString(localName, null, value, xsiType);
}
protected void WriteElementString(string localName, string? ns, string? value, XmlQualifiedName? xsiType)
{
if (value == null) return;
if (xsiType == null)
_w.WriteElementString(localName, ns, value);
else
{
_w.WriteStartElement(localName, ns);
WriteXsiType(xsiType.Name, xsiType.Namespace);
_w.WriteString(value);
_w.WriteEndElement();
}
}
protected void WriteElementStringRaw(string localName, string? value)
{
WriteElementStringRaw(localName, null, value, null);
}
protected void WriteElementStringRaw(string localName, byte[]? value)
{
WriteElementStringRaw(localName, null, value, null);
}
protected void WriteElementStringRaw(string localName, string? ns, string? value)
{
WriteElementStringRaw(localName, ns, value, null);
}
protected void WriteElementStringRaw(string localName, string? ns, byte[]? value)
{
WriteElementStringRaw(localName, ns, value, null);
}
protected void WriteElementStringRaw(string localName, string? value, XmlQualifiedName? xsiType)
{
WriteElementStringRaw(localName, null, value, xsiType);
}
protected void WriteElementStringRaw(string localName, byte[]? value, XmlQualifiedName? xsiType)
{
WriteElementStringRaw(localName, null, value, xsiType);
}
protected void WriteElementStringRaw(string localName, string? ns, string? value, XmlQualifiedName? xsiType)
{
if (value == null) return;
_w.WriteStartElement(localName, ns);
if (xsiType != null)
WriteXsiType(xsiType.Name, xsiType.Namespace);
_w.WriteRaw(value);
_w.WriteEndElement();
}
protected void WriteElementStringRaw(string localName, string? ns, byte[]? value, XmlQualifiedName? xsiType)
{
if (value == null) return;
_w.WriteStartElement(localName, ns);
if (xsiType != null)
WriteXsiType(xsiType.Name, xsiType.Namespace);
XmlCustomFormatter.WriteArrayBase64(_w, value, 0, value.Length);
_w.WriteEndElement();
}
protected void WriteRpcResult(string name, string? ns)
{
if (!_soap12) return;
WriteElementQualifiedName(Soap12.RpcResult, Soap12.RpcNamespace, new XmlQualifiedName(name, ns), null);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected void WriteElementQualifiedName(string localName, XmlQualifiedName? value)
{
WriteElementQualifiedName(localName, null, value, null);
}
protected void WriteElementQualifiedName(string localName, XmlQualifiedName? value, XmlQualifiedName? xsiType)
{
WriteElementQualifiedName(localName, null, value, xsiType);
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected void WriteElementQualifiedName(string localName, string? ns, XmlQualifiedName? value)
{
WriteElementQualifiedName(localName, ns, value, null);
}
protected void WriteElementQualifiedName(string localName, string? ns, XmlQualifiedName? value, XmlQualifiedName? xsiType)
{
if (value == null) return;
if (string.IsNullOrEmpty(value.Namespace))
{
WriteStartElement(localName, ns, null, true);
WriteAttribute("xmlns", "");
}
else
_w.WriteStartElement(localName, ns);
if (xsiType != null)
WriteXsiType(xsiType.Name, xsiType.Namespace);
_w.WriteString(FromXmlQualifiedName(value, false));
_w.WriteEndElement();
}
protected void AddWriteCallback(Type type, string typeName, string? typeNs, XmlSerializationWriteCallback callback)
{
TypeEntry entry = new TypeEntry();
entry.typeName = typeName;
entry.typeNs = typeNs;
entry.type = type;
entry.callback = callback;
_typeEntries![type] = entry;
}
[RequiresUnreferencedCode("calls GetArrayElementType")]
private void WriteArray(string name, string? ns, object o, Type type)
{
Type elementType = TypeScope.GetArrayElementType(type, null)!;
string typeName;
string? typeNs;
StringBuilder arrayDims = new StringBuilder();
if (!_soap12)
{
while ((elementType.IsArray || typeof(IEnumerable).IsAssignableFrom(elementType)) && GetPrimitiveTypeName(elementType, false) == null)
{
elementType = TypeScope.GetArrayElementType(elementType, null)!;
arrayDims.Append("[]");
}
}
if (elementType == typeof(object))
{
typeName = Soap.UrType;
typeNs = XmlSchema.Namespace;
}
else
{
TypeEntry? entry = GetTypeEntry(elementType);
if (entry != null)
{
typeName = entry.typeName!;
typeNs = entry.typeNs;
}
else if (_soap12)
{
XmlQualifiedName? qualName = GetPrimitiveTypeName(elementType, false);
if (qualName != null)
{
typeName = qualName.Name;
typeNs = qualName.Namespace;
}
else
{
Type? elementBaseType = elementType.BaseType;
while (elementBaseType != null)
{
entry = GetTypeEntry(elementBaseType);
if (entry != null) break;
elementBaseType = elementBaseType.BaseType;
}
if (entry != null)
{
typeName = entry.typeName!;
typeNs = entry.typeNs;
}
else
{
typeName = Soap.UrType;
typeNs = XmlSchema.Namespace;
}
}
}
else
{
XmlQualifiedName qualName = GetPrimitiveTypeName(elementType);
typeName = qualName.Name;
typeNs = qualName.Namespace;
}
}
if (arrayDims.Length > 0)
typeName += arrayDims.ToString();
if (_soap12 && name != null && name.Length > 0)
WriteStartElement(name, ns, null, false);
else
WriteStartElement(Soap.Array, Soap.Encoding, null, true);
WriteId(o, false);
if (type.IsArray)
{
Array a = (Array)o;
int arrayLength = a.Length;
if (_soap12)
{
_w.WriteAttributeString("itemType", Soap12.Encoding, GetQualifiedName(typeName, typeNs));
_w.WriteAttributeString("arraySize", Soap12.Encoding, arrayLength.ToString(CultureInfo.InvariantCulture));
}
else
{
_w.WriteAttributeString("arrayType", Soap.Encoding, $"{GetQualifiedName(typeName, typeNs)}[{arrayLength}]");
}
for (int i = 0; i < arrayLength; i++)
{
WritePotentiallyReferencingElement("Item", "", a.GetValue(i), elementType, false, true);
}
}
else
{
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (!typeof(IEnumerable).IsAssignableFrom(type)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "not array like type " + type.FullName));
#endif
int arrayLength = typeof(ICollection).IsAssignableFrom(type) ? ((ICollection)o).Count : -1;
if (_soap12)
{
_w.WriteAttributeString("itemType", Soap12.Encoding, GetQualifiedName(typeName, typeNs));
if (arrayLength >= 0)
_w.WriteAttributeString("arraySize", Soap12.Encoding, arrayLength.ToString(CultureInfo.InvariantCulture));
}
else
{
string brackets = arrayLength >= 0 ? $"[{arrayLength}]" : "[]";
_w.WriteAttributeString("arrayType", Soap.Encoding, GetQualifiedName(typeName, typeNs) + brackets);
}
IEnumerator e = ((IEnumerable)o).GetEnumerator();
if (e != null)
{
while (e.MoveNext())
{
WritePotentiallyReferencingElement("Item", "", e.Current, elementType, false, true);
}
}
}
_w.WriteEndElement();
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o)
{
WritePotentiallyReferencingElement(n, ns, o, null, false, false);
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o, Type? ambientType)
{
WritePotentiallyReferencingElement(n, ns, o, ambientType, false, false);
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WritePotentiallyReferencingElement(string n, string? ns, object? o, Type? ambientType, bool suppressReference)
{
WritePotentiallyReferencingElement(n, ns, o, ambientType, suppressReference, false);
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WritePotentiallyReferencingElement(string? n, string? ns, object? o, Type? ambientType, bool suppressReference, bool isNullable)
{
if (o == null)
{
if (isNullable) WriteNullTagEncoded(n, ns);
return;
}
Type t = o.GetType();
if (Type.GetTypeCode(t) == TypeCode.Object && !(o is Guid) && (t != typeof(XmlQualifiedName)) && !(o is XmlNode[]) && (t != typeof(byte[])))
{
if ((suppressReference || _soap12) && !IsIdDefined(o))
{
WriteReferencedElement(n, ns, o, ambientType);
}
else
{
if (n == null)
{
TypeEntry entry = GetTypeEntry(t)!;
WriteReferencingElement(entry.typeName!, entry.typeNs, o, isNullable);
}
else
WriteReferencingElement(n, ns, o, isNullable);
}
}
else
{
// Enums always write xsi:type, so don't write it again here.
bool needXsiType = t != ambientType && !t.IsEnum;
TypeEntry? entry = GetTypeEntry(t);
if (entry != null)
{
if (n == null)
WriteStartElement(entry.typeName!, entry.typeNs, null, true);
else
WriteStartElement(n, ns, null, true);
if (needXsiType) WriteXsiType(entry.typeName!, entry.typeNs);
entry.callback!(o);
_w.WriteEndElement();
}
else
{
WriteTypedPrimitive(n, ns, o, needXsiType);
}
}
}
[RequiresUnreferencedCode("calls WriteReferencedElement")]
private void WriteReferencedElement(object o, Type? ambientType)
{
WriteReferencedElement(null, null, o, ambientType);
}
[RequiresUnreferencedCode("calls WriteArray")]
private void WriteReferencedElement(string? name, string? ns, object o, Type? ambientType)
{
name ??= string.Empty;
Type t = o.GetType();
if (t.IsArray || typeof(IEnumerable).IsAssignableFrom(t))
{
WriteArray(name, ns, o, t);
}
else
{
TypeEntry? entry = GetTypeEntry(t);
if (entry == null) throw CreateUnknownTypeException(t);
WriteStartElement(name.Length == 0 ? entry.typeName! : name!, ns ?? entry.typeNs, null, true);
WriteId(o, false);
if (ambientType != t) WriteXsiType(entry.typeName!, entry.typeNs);
entry.callback!(o);
_w.WriteEndElement();
}
}
[RequiresUnreferencedCode("calls InitCallbacks")]
private TypeEntry? GetTypeEntry(Type t)
{
if (_typeEntries == null)
{
_typeEntries = new Hashtable();
InitCallbacks();
}
return (TypeEntry?)_typeEntries[t];
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected abstract void InitCallbacks();
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
protected void WriteReferencedElements()
{
if (_referencesToWrite == null) return;
for (int i = 0; i < _referencesToWrite.Count; i++)
{
WriteReferencedElement(_referencesToWrite[i]!, null);
}
}
protected void TopLevelElement()
{
_objectsInUse = new Hashtable();
}
///<internalonly/>
protected void WriteNamespaceDeclarations(XmlSerializerNamespaces? xmlns)
{
if (xmlns != null)
{
foreach (XmlQualifiedName qname in xmlns.Namespaces)
{
string prefix = qname.Name;
string? ns = qname.Namespace;
if (_namespaces != null)
{
string? oldNs;
if (_namespaces.TryLookupNamespace(prefix, out oldNs) && oldNs != null && oldNs != ns)
{
throw new InvalidOperationException(SR.Format(SR.XmlDuplicateNs, prefix, ns));
}
}
string? oldPrefix = string.IsNullOrEmpty(ns) ? null : Writer.LookupPrefix(ns);
if (oldPrefix == null || oldPrefix != prefix)
{
WriteAttribute(nameof(xmlns), prefix, null, ns);
}
}
}
_namespaces = null;
}
private string NextPrefix()
{
if (_usedPrefixes == null)
{
return _aliasBase + (++_tempNamespacePrefix);
}
while (_usedPrefixes.Contains(++_tempNamespacePrefix)) { }
return _aliasBase + _tempNamespacePrefix;
}
internal sealed class TypeEntry
{
internal XmlSerializationWriteCallback? callback;
internal string? typeNs;
internal string? typeName;
internal Type? type;
}
}
///<internalonly/>
public delegate void XmlSerializationWriteCallback(object o);
internal static class DynamicAssemblies
{
private static readonly Hashtable s_nameToAssemblyMap = new Hashtable();
private static readonly Hashtable s_assemblyToNameMap = new Hashtable();
private static readonly ContextAwareTables<object> s_tableIsTypeDynamic = new ContextAwareTables<object>();
// SxS: This method does not take any resource name and does not expose any resources to the caller.
// It's OK to suppress the SxS warning.
internal static bool IsTypeDynamic(Type type)
{
object oIsTypeDynamic = s_tableIsTypeDynamic.GetOrCreateValue(type, static type =>
{
Assembly assembly = type.Assembly;
bool isTypeDynamic = assembly.IsDynamic /*|| string.IsNullOrEmpty(assembly.Location)*/;
if (!isTypeDynamic)
{
if (type.IsArray)
{
isTypeDynamic = IsTypeDynamic(type.GetElementType()!);
}
else if (type.IsGenericType)
{
Type[] parameterTypes = type.GetGenericArguments();
if (parameterTypes != null)
{
for (int i = 0; i < parameterTypes.Length; i++)
{
Type parameterType = parameterTypes[i];
if (!(parameterType == null || parameterType.IsGenericParameter))
{
isTypeDynamic = IsTypeDynamic(parameterType);
if (isTypeDynamic)
break;
}
}
}
}
}
return isTypeDynamic;
});
return (bool)oIsTypeDynamic;
}
internal static bool IsTypeDynamic(Type[] arguments)
{
foreach (Type t in arguments)
{
if (DynamicAssemblies.IsTypeDynamic(t))
{
return true;
}
}
return false;
}
internal static void Add(Assembly a)
{
lock (s_nameToAssemblyMap)
{
if (s_assemblyToNameMap[a] != null)
{
//already added
return;
}
Assembly? oldAssembly = s_nameToAssemblyMap[a.FullName!] as Assembly;
string? key = null;
if (oldAssembly == null)
{
key = a.FullName;
}
else if (oldAssembly != a)
{
//more than one assembly with same name
key = $"{a.FullName}, {s_nameToAssemblyMap.Count}";
}
if (key != null)
{
s_nameToAssemblyMap.Add(key, a);
s_assemblyToNameMap.Add(a, key);
}
}
}
internal static Assembly? Get(string fullName)
{
return s_nameToAssemblyMap != null ? (Assembly?)s_nameToAssemblyMap[fullName] : null;
}
internal static string? GetName(Assembly a)
{
return s_assemblyToNameMap != null ? (string?)s_assemblyToNameMap[a] : null;
}
}
internal sealed class ReflectionAwareCodeGen
{
private const string arrayMemberKey = "0";
// reflectionVariables holds mapping between a reflection entity
// referenced in the generated code (such as TypeInfo,
// FieldInfo) and the variable which represent the entity (and
// initialized before).
// The types of reflection entity and corresponding key is
// given below.
// ----------------------------------------------------------------------------------
// Entity Key
// ----------------------------------------------------------------------------------
// Assembly assembly.FullName
// Type CodeIdentifier.EscapedKeywords(type.FullName)
// Field fieldName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName>)
// Property propertyName+":"+CodeIdentifier.EscapedKeywords(containingType.FullName)
// ArrayAccessor "0:"+CodeIdentifier.EscapedKeywords(typeof(Array).FullName)
// MyCollectionAccessor "0:"+CodeIdentifier.EscapedKeywords(typeof(MyCollection).FullName)
// ----------------------------------------------------------------------------------
private Hashtable _reflectionVariables = null!;
private int _nextReflectionVariableNumber;
private readonly IndentedWriter _writer;
internal ReflectionAwareCodeGen(IndentedWriter writer)
{
_writer = writer;
}
[RequiresUnreferencedCode("calls GetTypeDesc")]
internal void WriteReflectionInit(TypeScope scope)
{
foreach (Type type in scope.Types)
{
TypeDesc typeDesc = scope.GetTypeDesc(type);
if (typeDesc.UseReflection)
WriteTypeInfo(scope, typeDesc, type);
}
}
[RequiresUnreferencedCode(XmlSerializer.TrimSerializationWarning)]
private string WriteTypeInfo(TypeScope scope, TypeDesc typeDesc, Type type)
{
InitTheFirstTime();
string typeFullName = typeDesc.CSharpName;
string? typeVariable = (string?)_reflectionVariables[typeFullName];
if (typeVariable != null)
return typeVariable;
if (type.IsArray)
{
typeVariable = GenerateVariableName("array", typeDesc.CSharpName);
TypeDesc elementTypeDesc = typeDesc.ArrayElementTypeDesc!;
if (elementTypeDesc.UseReflection)
{
string elementTypeVariable = WriteTypeInfo(scope, elementTypeDesc, scope.GetTypeFromTypeDesc(elementTypeDesc)!);
_writer.WriteLine($"static {typeof(Type).FullName} {typeVariable} = {elementTypeVariable}.MakeArrayType();");
}
else
{
string assemblyVariable = WriteAssemblyInfo(type);
_writer.Write($"static {typeof(Type).FullName} {typeVariable} = {assemblyVariable}.GetType(");
WriteQuotedCSharpString(type.FullName);
_writer.WriteLine(");");
}
}
else
{
typeVariable = GenerateVariableName(nameof(type), typeDesc.CSharpName);
Type? parameterType = Nullable.GetUnderlyingType(type);
if (parameterType != null)
{
string parameterTypeVariable = WriteTypeInfo(scope, scope.GetTypeDesc(parameterType), parameterType);
_writer.WriteLine($"static {typeof(Type).FullName} {typeVariable} = typeof(System.Nullable<>).MakeGenericType(new {typeof(Type).FullName}[] {{{parameterTypeVariable}}});");
}
else
{
string assemblyVariable = WriteAssemblyInfo(type);
_writer.Write($"static {typeof(Type).FullName} {typeVariable} = {assemblyVariable}.GetType(");
WriteQuotedCSharpString(type.FullName);
_writer.WriteLine(");");
}
}
_reflectionVariables.Add(typeFullName, typeVariable);
TypeMapping? mapping = scope.GetTypeMappingFromTypeDesc(typeDesc);
if (mapping != null)
WriteMappingInfo(mapping, typeVariable, type);
if (typeDesc.IsCollection || typeDesc.IsEnumerable)
{// Arrays use the generic item_Array
TypeDesc elementTypeDesc = typeDesc.ArrayElementTypeDesc!;
if (elementTypeDesc.UseReflection)
WriteTypeInfo(scope, elementTypeDesc, scope.GetTypeFromTypeDesc(elementTypeDesc)!);
WriteCollectionInfo(typeVariable, typeDesc, type);
}
return typeVariable;
}
[MemberNotNull(nameof(_reflectionVariables))]
private void InitTheFirstTime()
{
if (_reflectionVariables == null)
{
_reflectionVariables = new Hashtable();
_writer.Write(string.Format(CultureInfo.InvariantCulture, HelperClassesForUseReflection,
"object", "string", typeof(Type).FullName,
typeof(FieldInfo).FullName, typeof(PropertyInfo).FullName));
WriteDefaultIndexerInit(typeof(IList), typeof(Array).FullName!, false, false);
}
}
private void WriteMappingInfo(TypeMapping mapping, string typeVariable,
[DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type)
{
string typeFullName = mapping.TypeDesc!.CSharpName;
if (mapping is StructMapping)
{
StructMapping structMapping = (mapping as StructMapping)!;
for (int i = 0; i < structMapping.Members!.Length; i++)
{
MemberMapping member = structMapping.Members[i];
WriteMemberInfo(type, typeFullName, typeVariable, member.Name);
if (member.CheckShouldPersist)
{
string memberName = $"ShouldSerialize{member.Name}";
WriteMethodInfo(typeFullName, typeVariable, memberName, false);
}
if (member.CheckSpecified != SpecifiedAccessor.None)
{
string memberName = $"{member.Name}Specified";
WriteMemberInfo(type, typeFullName, typeVariable, memberName);
}
if (member.ChoiceIdentifier != null)
{
string memberName = member.ChoiceIdentifier.MemberName!;
WriteMemberInfo(type, typeFullName, typeVariable, memberName);
}
}
}
else if (mapping is EnumMapping)
{
FieldInfo[] enumFields = type.GetFields();
for (int i = 0; i < enumFields.Length; i++)
{
WriteMemberInfo(type, typeFullName, typeVariable, enumFields[i].Name);
}
}
}
private void WriteCollectionInfo(string typeVariable, TypeDesc typeDesc,
[DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type)
{
string typeFullName = CodeIdentifier.GetCSharpName(type);
string elementTypeFullName = typeDesc.ArrayElementTypeDesc!.CSharpName;
bool elementUseReflection = typeDesc.ArrayElementTypeDesc.UseReflection;
if (typeDesc.IsCollection)
{
WriteDefaultIndexerInit(type, typeFullName, typeDesc.UseReflection, elementUseReflection);
}
else if (typeDesc.IsEnumerable)
{
if (typeDesc.IsGenericInterface)
{
WriteMethodInfo(typeFullName, typeVariable, "System.Collections.Generic.IEnumerable*", true);
}
else if (!typeDesc.IsPrivateImplementation)
{
WriteMethodInfo(typeFullName, typeVariable, "GetEnumerator", true);
}
}
WriteMethodInfo(typeFullName, typeVariable, "Add", false, GetStringForTypeof(elementTypeFullName, elementUseReflection));
}
private string WriteAssemblyInfo(Type type)
{
string assemblyFullName = type.Assembly.FullName!;
string? assemblyVariable = (string?)_reflectionVariables[assemblyFullName];
if (assemblyVariable == null)
{
int iComma = assemblyFullName.IndexOf(',');
string assemblyName = (iComma > -1) ? assemblyFullName.Substring(0, iComma) : assemblyFullName;
assemblyVariable = GenerateVariableName("assembly", assemblyName);
//writer.WriteLine("static "+ typeof(Assembly).FullName+" "+assemblyVariable+" = "+typeof(Assembly).FullName+".Load(");
_writer.Write($"static {typeof(Assembly).FullName} {assemblyVariable} = ResolveDynamicAssembly(");
WriteQuotedCSharpString(DynamicAssemblies.GetName(type.Assembly)/*assemblyFullName*/);
_writer.WriteLine(");");
_reflectionVariables.Add(assemblyFullName, assemblyVariable);
}
return assemblyVariable;
}
private string WriteMemberInfo(
[DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, string escapedName, string typeVariable, string memberName)
{
MemberInfo[] memberInfos = type.GetMember(memberName);
for (int i = 0; i < memberInfos.Length; i++)
{
if (memberInfos[i] is PropertyInfo)
{
string propVariable = GenerateVariableName("prop", memberName);
_writer.Write($"static XSPropInfo {propVariable} = new XSPropInfo({typeVariable}, ");
WriteQuotedCSharpString(memberName);
_writer.WriteLine(");");
_reflectionVariables.Add($"{memberName}:{escapedName}", propVariable);
return propVariable;
}
else if (memberInfos[i] is FieldInfo)
{
string fieldVariable = GenerateVariableName("field", memberName);
_writer.Write($"static XSFieldInfo {fieldVariable} = new XSFieldInfo({typeVariable}, ");
WriteQuotedCSharpString(memberName);
_writer.WriteLine(");");
_reflectionVariables.Add($"{memberName}:{escapedName}", fieldVariable);
return fieldVariable;
}
}
throw new InvalidOperationException(SR.Format(SR.XmlSerializerUnsupportedType, memberInfos[0]));
}
private string WriteMethodInfo(string escapedName, string typeVariable, string memberName, bool isNonPublic, params string[] paramTypes)
{
string methodVariable = GenerateVariableName("method", memberName);
_writer.Write($"static {typeof(MethodInfo).FullName} {methodVariable} = {typeVariable}.GetMethod(");
WriteQuotedCSharpString(memberName);
_writer.Write(", ");
string bindingFlags = typeof(BindingFlags).FullName!;
_writer.Write(bindingFlags);
_writer.Write(".Public | ");
_writer.Write(bindingFlags);
_writer.Write(".Instance | ");
_writer.Write(bindingFlags);
_writer.Write(".Static");
if (isNonPublic)
{
_writer.Write(" | ");
_writer.Write(bindingFlags);
_writer.Write(".NonPublic");
}
_writer.Write(", null, ");
_writer.Write($"new {typeof(Type).FullName}[] {{ ");
for (int i = 0; i < paramTypes.Length; i++)
{
_writer.Write(paramTypes[i]);
if (i < (paramTypes.Length - 1))
_writer.Write(", ");
}
_writer.WriteLine("}, null);");
_reflectionVariables.Add($"{memberName}:{escapedName}", methodVariable);
return methodVariable;
}
private string WriteDefaultIndexerInit(
[DynamicallyAccessedMembers(TrimmerConstants.PublicMembers)] Type type, string escapedName, bool collectionUseReflection, bool elementUseReflection)
{
string itemVariable = GenerateVariableName("item", escapedName);
PropertyInfo defaultIndexer = TypeScope.GetDefaultIndexer(type, null);
_writer.Write("static XSArrayInfo ");
_writer.Write(itemVariable);
_writer.Write("= new XSArrayInfo(");
_writer.Write(GetStringForTypeof(CodeIdentifier.GetCSharpName(type), collectionUseReflection));
_writer.Write(".GetProperty(");
WriteQuotedCSharpString(defaultIndexer.Name);
_writer.Write(",");
//defaultIndexer.PropertyType is same as TypeDesc.ElementTypeDesc
_writer.Write(GetStringForTypeof(CodeIdentifier.GetCSharpName(defaultIndexer.PropertyType), elementUseReflection));
_writer.Write(",new ");
_writer.Write(typeof(Type[]).FullName);
_writer.WriteLine("{typeof(int)}));");
_reflectionVariables.Add($"{arrayMemberKey}:{escapedName}", itemVariable);
return itemVariable;
}
private string GenerateVariableName(string prefix, string fullName)
{
++_nextReflectionVariableNumber;
return $"{prefix}{_nextReflectionVariableNumber}_{CodeIdentifier.MakeValidInternal(fullName.Replace('.', '_'))}";
}
internal string? GetReflectionVariable(string typeFullName, string? memberName)
{
string key;
if (memberName == null)
key = typeFullName;
else
key = $"{memberName}:{typeFullName}";
return (string?)_reflectionVariables[key];
}
internal string GetStringForMethodInvoke(string obj, string escapedTypeName, string methodName, bool useReflection, params string[] args)
{
StringBuilder sb = new StringBuilder();
if (useReflection)
{
sb.Append(GetReflectionVariable(escapedTypeName, methodName));
sb.Append(".Invoke(");
sb.Append(obj);
sb.Append(", new object[] {");
}
else
{
sb.Append(obj);
sb.Append(".@");
sb.Append(methodName);
sb.Append('(');
}
for (int i = 0; i < args.Length; i++)
{
if (i != 0)
sb.Append(", ");
sb.Append(args[i]);
}
if (useReflection)
sb.Append("})");
else
sb.Append(')');
return sb.ToString();
}
internal string GetStringForEnumCompare(EnumMapping mapping, string memberName, bool useReflection)
{
if (!useReflection)
{
CodeIdentifier.CheckValidIdentifier(memberName);
return $"{mapping.TypeDesc!.CSharpName}.@{memberName}";
}
string memberAccess = GetStringForEnumMember(mapping.TypeDesc!.CSharpName, memberName, useReflection);
return GetStringForEnumLongValue(memberAccess, useReflection);
}
internal static string GetStringForEnumLongValue(string variable, bool useReflection)
{
if (useReflection)
return $"{typeof(Convert).FullName}.ToInt64({variable})";
return $"(({typeof(long).FullName}){variable})";
}
internal string GetStringForTypeof(string typeFullName, bool useReflection)
{
if (useReflection)
{
return GetReflectionVariable(typeFullName, null)!;
}
else
{
return $"typeof({typeFullName})";
}
}
internal string GetStringForMember(string obj, string memberName, TypeDesc typeDesc)
{
if (!typeDesc.UseReflection)
return $"{obj}.@{memberName}";
while (typeDesc != null)
{
string typeFullName = typeDesc.CSharpName;
string? memberInfoName = GetReflectionVariable(typeFullName, memberName);
if (memberInfoName != null)
return $"{memberInfoName}[{obj}]";
// member may be part of the basetype
typeDesc = typeDesc.BaseTypeDesc!;
if (typeDesc != null && !typeDesc.UseReflection)
return $"(({typeDesc.CSharpName}){obj}).@{memberName}";
}
//throw GetReflectionVariableException(saveTypeDesc.CSharpName,memberName);
// NOTE, sowmys:Must never happen. If it does let the code
// gen continue to help debugging what's gone wrong.
// Eventually the compilation will fail.
return $"[{obj}]";
}
/*
Exception GetReflectionVariableException(string typeFullName, string memberName){
string key;
if (memberName == null)
key = typeFullName;
else
key = memberName+":"+typeFullName;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (object varAvail in reflectionVariables.Keys){
sb.Append(varAvail.ToString());
sb.Append("\n");
}
return new Exception("No reflection variable for " + key + "\nAvailable keys\n"+sb.ToString());
}*/
internal string GetStringForEnumMember(string typeFullName, string memberName, bool useReflection)
{
if (!useReflection)
return $"{typeFullName}.@{memberName}";
string? memberInfoName = GetReflectionVariable(typeFullName, memberName);
return $"{memberInfoName}[null]";
}
internal string GetStringForArrayMember(string arrayName, string subscript, TypeDesc arrayTypeDesc)
{
if (!arrayTypeDesc.UseReflection)
{
return $"{arrayName}[{subscript}]";
}
string typeFullName = arrayTypeDesc.IsCollection ? arrayTypeDesc.CSharpName : typeof(Array).FullName!;
string? arrayInfo = GetReflectionVariable(typeFullName, arrayMemberKey);
return $"{arrayInfo}[{arrayName}, {subscript}]";
}
internal string GetStringForMethod(string obj, string typeFullName, string memberName, bool useReflection)
{
if (!useReflection)
return $"{obj}.{memberName}(";
string? memberInfoName = GetReflectionVariable(typeFullName, memberName);
return $"{memberInfoName}.Invoke({obj}, new object[]{{";
}
internal string GetStringForCreateInstance(string escapedTypeName, bool useReflection, bool ctorInaccessible, bool cast)
{
return GetStringForCreateInstance(escapedTypeName, useReflection, ctorInaccessible, cast, string.Empty);
}
internal string GetStringForCreateInstance(string escapedTypeName, bool useReflection, bool ctorInaccessible, bool cast, string arg)
{
if (!useReflection && !ctorInaccessible)
return $"new {escapedTypeName}({arg})";
return GetStringForCreateInstance(GetStringForTypeof(escapedTypeName, useReflection), cast && !useReflection ? escapedTypeName : null, ctorInaccessible, arg);
}
internal static string GetStringForCreateInstance(string type, string? cast, bool nonPublic, string? arg)
{
StringBuilder createInstance = new StringBuilder();
if (cast != null && cast.Length > 0)
{
createInstance.Append('(');
createInstance.Append(cast);
createInstance.Append(')');
}
createInstance.Append(typeof(Activator).FullName);
createInstance.Append(".CreateInstance(");
createInstance.Append(type);
createInstance.Append(", ");
string bindingFlags = typeof(BindingFlags).FullName!;
createInstance.Append(bindingFlags);
createInstance.Append(".Instance | ");
createInstance.Append(bindingFlags);
createInstance.Append(".Public | ");
createInstance.Append(bindingFlags);
createInstance.Append(".CreateInstance");
if (nonPublic)
{
createInstance.Append(" | ");
createInstance.Append(bindingFlags);
createInstance.Append(".NonPublic");
}
if (string.IsNullOrEmpty(arg))
{
createInstance.Append(", null, new object[0], null)");
}
else
{
createInstance.Append(", null, new object[] { ");
createInstance.Append(arg);
createInstance.Append(" }, null)");
}
return createInstance.ToString();
}
internal void WriteLocalDecl(string typeFullName, string variableName, string initValue, bool useReflection)
{
if (useReflection)
typeFullName = "object";
_writer.Write(typeFullName);
_writer.Write(" ");
_writer.Write(variableName);
if (initValue != null)
{
_writer.Write(" = ");
if (!useReflection && initValue != "null")
{
_writer.Write($"({typeFullName})");
}
_writer.Write(initValue);
}
_writer.WriteLine(";");
}
internal void WriteCreateInstance(string escapedName, string source, bool useReflection, bool ctorInaccessible)
{
_writer.Write(useReflection ? "object" : escapedName);
_writer.Write(" ");
_writer.Write(source);
_writer.Write(" = ");
_writer.Write(GetStringForCreateInstance(escapedName, useReflection, ctorInaccessible, !useReflection && ctorInaccessible));
_writer.WriteLine(";");
}
internal void WriteInstanceOf(string source, string escapedTypeName, bool useReflection)
{
if (!useReflection)
{
_writer.Write(source);
_writer.Write(" is ");
_writer.Write(escapedTypeName);
return;
}
_writer.Write(GetReflectionVariable(escapedTypeName, null));
_writer.Write(".IsAssignableFrom(");
_writer.Write(source);
_writer.Write(".GetType())");
}
internal void WriteArrayLocalDecl(string typeName, string variableName, string? initValue, TypeDesc arrayTypeDesc)
{
if (arrayTypeDesc.UseReflection)
{
if (arrayTypeDesc.IsEnumerable)
typeName = typeof(IEnumerable).FullName!;
else if (arrayTypeDesc.IsCollection)
typeName = typeof(ICollection).FullName!;
else
typeName = typeof(Array).FullName!;
}
_writer.Write(typeName);
_writer.Write(" ");
_writer.Write(variableName);
if (initValue != null)
{
_writer.Write(" = ");
if (initValue != "null")
_writer.Write($"({typeName})");
_writer.Write(initValue);
}
_writer.WriteLine(";");
}
internal void WriteEnumCase(string fullTypeName, ConstantMapping c, bool useReflection)
{
_writer.Write("case ");
if (useReflection)
{
_writer.Write(c.Value.ToString(CultureInfo.InvariantCulture));
}
else
{
_writer.Write(fullTypeName);
_writer.Write(".@");
CodeIdentifier.CheckValidIdentifier(c.Name);
_writer.Write(c.Name);
}
_writer.Write(": ");
}
internal void WriteTypeCompare(string variable, string escapedTypeName, bool useReflection)
{
_writer.Write(variable);
_writer.Write(" == ");
_writer.Write(GetStringForTypeof(escapedTypeName, useReflection));
}
internal void WriteArrayTypeCompare(string variable, string escapedTypeName, string elementTypeName, bool useReflection)
{
if (!useReflection)
{
_writer.Write(variable);
_writer.Write(" == typeof(");
_writer.Write(escapedTypeName);
_writer.Write(")");
return;
}
_writer.Write(variable);
_writer.Write(".IsArray ");
_writer.Write(" && ");
WriteTypeCompare($"{variable}.GetElementType()", elementTypeName, useReflection);
}
internal static void WriteQuotedCSharpString(IndentedWriter writer, string? value)
{
if (value == null)
{
writer.Write("null");
return;
}
writer.Write("@\"");
foreach (char ch in value)
{
if (ch < 32)
{
if (ch == '\r')
writer.Write("\\r");
else if (ch == '\n')
writer.Write("\\n");
else if (ch == '\t')
writer.Write("\\t");
else
{
byte b = (byte)ch;
writer.Write("\\x");
writer.Write(HexConverter.ToCharUpper(b >> 4));
writer.Write(HexConverter.ToCharUpper(b));
}
}
else if (ch == '\"')
{
writer.Write("\"\"");
}
else
{
writer.Write(ch);
}
}
writer.Write("\"");
}
internal void WriteQuotedCSharpString(string? value)
{
WriteQuotedCSharpString(_writer, value);
}
private const string HelperClassesForUseReflection = @"
sealed class XSFieldInfo {{
{3} fieldInfo;
public XSFieldInfo({2} t, {1} memberName){{
fieldInfo = t.GetField(memberName);
}}
public {0} this[{0} o] {{
get {{
return fieldInfo.GetValue(o);
}}
set {{
fieldInfo.SetValue(o, value);
}}
}}
}}
sealed class XSPropInfo {{
{4} propInfo;
public XSPropInfo({2} t, {1} memberName){{
propInfo = t.GetProperty(memberName);
}}
public {0} this[{0} o] {{
get {{
return propInfo.GetValue(o, null);
}}
set {{
propInfo.SetValue(o, value, null);
}}
}}
}}
sealed class XSArrayInfo {{
{4} propInfo;
public XSArrayInfo({4} propInfo){{
this.propInfo = propInfo;
}}
public {0} this[{0} a, int i] {{
get {{
return propInfo.GetValue(a, new {0}[]{{i}});
}}
set {{
propInfo.SetValue(a, value, new {0}[]{{i}});
}}
}}
}}
";
}
internal sealed class XmlSerializationWriterCodeGen : XmlSerializationCodeGen
{
[RequiresUnreferencedCode("creates XmlSerializationCodeGen")]
internal XmlSerializationWriterCodeGen(IndentedWriter writer, TypeScope[] scopes, string access, string className) : base(writer, scopes, access, className)
{
}
[RequiresUnreferencedCode("calls WriteStructMethod")]
internal void GenerateBegin()
{
Writer.Write(Access);
Writer.Write(" class ");
Writer.Write(ClassName);
Writer.Write(" : ");
Writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriter).FullName);
Writer.WriteLine(" {");
Writer.Indent++;
foreach (TypeScope scope in Scopes)
{
foreach (TypeMapping mapping in scope.TypeMappings)
{
if (mapping is StructMapping || mapping is EnumMapping)
{
MethodNames.Add(mapping, NextMethodName(mapping.TypeDesc!.Name));
}
}
RaCodeGen.WriteReflectionInit(scope);
}
// pre-generate write methods only for the encoded soap
foreach (TypeScope scope in Scopes)
{
foreach (TypeMapping mapping in scope.TypeMappings)
{
if (!mapping.IsSoap)
continue;
if (mapping is StructMapping)
WriteStructMethod((StructMapping)mapping);
else if (mapping is EnumMapping)
WriteEnumMethod((EnumMapping)mapping);
}
}
}
[RequiresUnreferencedCode("calls WriteStructMethod")]
internal override void GenerateMethod(TypeMapping mapping)
{
if (GeneratedMethods.Contains(mapping))
return;
GeneratedMethods[mapping] = mapping;
if (mapping is StructMapping)
{
WriteStructMethod((StructMapping)mapping);
}
else if (mapping is EnumMapping)
{
WriteEnumMethod((EnumMapping)mapping);
}
}
[RequiresUnreferencedCode("calls GenerateReferencedMethods")]
internal void GenerateEnd()
{
GenerateReferencedMethods();
GenerateInitCallbacksMethod();
Writer.Indent--;
Writer.WriteLine("}");
}
[RequiresUnreferencedCode("calls GenerateMembersElement")]
internal string? GenerateElement(XmlMapping xmlMapping)
{
if (!xmlMapping.IsWriteable)
return null;
if (!xmlMapping.GenerateSerializer)
throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping));
if (xmlMapping is XmlTypeMapping)
return GenerateTypeElement((XmlTypeMapping)xmlMapping);
else if (xmlMapping is XmlMembersMapping)
return GenerateMembersElement((XmlMembersMapping)xmlMapping);
else
throw new ArgumentException(SR.XmlInternalError, nameof(xmlMapping));
}
private void GenerateInitCallbacksMethod()
{
Writer.WriteLine();
Writer.WriteLine("protected override void InitCallbacks() {");
Writer.Indent++;
foreach (TypeScope scope in Scopes)
{
foreach (TypeMapping typeMapping in scope.TypeMappings)
{
if (typeMapping.IsSoap &&
(typeMapping is StructMapping || typeMapping is EnumMapping) &&
!typeMapping.TypeDesc!.IsRoot)
{
string methodName = (string)MethodNames[typeMapping]!;
Writer.Write("AddWriteCallback(");
Writer.Write(RaCodeGen.GetStringForTypeof(typeMapping.TypeDesc.CSharpName, typeMapping.TypeDesc.UseReflection));
Writer.Write(", ");
WriteQuotedCSharpString(typeMapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(typeMapping.Namespace);
Writer.Write(", new ");
Writer.Write(typeof(System.Xml.Serialization.XmlSerializationWriteCallback).FullName);
Writer.Write("(this.");
Writer.Write(methodName);
Writer.WriteLine("));");
}
}
}
Writer.Indent--;
Writer.WriteLine("}");
}
[RequiresUnreferencedCode("calls WriteCheckDefault")]
private void WriteQualifiedNameElement(string name, string? ns, object? defaultValue, string source, bool nullable, bool IsSoap, TypeMapping mapping)
{
bool hasDefault = defaultValue != null && defaultValue != DBNull.Value;
if (hasDefault)
{
WriteCheckDefault(mapping, source, defaultValue!, nullable);
Writer.WriteLine(" {");
Writer.Indent++;
}
string suffix = IsSoap ? "Encoded" : "Literal";
Writer.Write(nullable ? ("WriteNullableQualifiedName" + suffix) : "WriteElementQualifiedName");
Writer.Write("(");
WriteQuotedCSharpString(name);
if (ns != null)
{
Writer.Write(", ");
WriteQuotedCSharpString(ns);
}
Writer.Write(", ");
Writer.Write(source);
if (IsSoap)
{
Writer.Write(", new System.Xml.XmlQualifiedName(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.Write(")");
}
Writer.WriteLine(");");
if (hasDefault)
{
Writer.Indent--;
Writer.WriteLine("}");
}
}
private void WriteEnumValue(EnumMapping mapping, string source)
{
string? methodName = ReferenceMapping(mapping);
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc!.Name) + Environment.StackTrace);
#endif
Writer.Write(methodName);
Writer.Write("(");
Writer.Write(source);
Writer.Write(")");
}
private void WritePrimitiveValue(TypeDesc typeDesc, string source)
{
if (typeDesc == StringTypeDesc || typeDesc.FormatterName == "String")
{
Writer.Write(source);
}
else
{
if (!typeDesc.HasCustomFormatter)
{
Writer.Write(typeof(XmlConvert).FullName);
Writer.Write(".ToString((");
Writer.Write(typeDesc.CSharpName);
Writer.Write(")");
Writer.Write(source);
Writer.Write(")");
}
else
{
Writer.Write("From");
Writer.Write(typeDesc.FormatterName);
Writer.Write("(");
Writer.Write(source);
Writer.Write(")");
}
}
}
[RequiresUnreferencedCode("calls WriteCheckDefault")]
private void WritePrimitive(string method, string name, string? ns, object? defaultValue, string source, TypeMapping mapping, bool writeXsiType, bool isElement, bool isNullable)
{
TypeDesc typeDesc = mapping.TypeDesc!;
bool hasDefault = defaultValue != null && defaultValue != DBNull.Value && mapping.TypeDesc!.HasDefaultSupport;
if (hasDefault)
{
if (mapping is EnumMapping)
{
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (defaultValue!.GetType() != typeof(string)) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, name + " has invalid default type " + defaultValue.GetType().Name));
#endif
Writer.Write("if (");
if (mapping.TypeDesc!.UseReflection)
Writer.Write(ReflectionAwareCodeGen.GetStringForEnumLongValue(source, mapping.TypeDesc.UseReflection));
else
Writer.Write(source);
Writer.Write(" != ");
if (((EnumMapping)mapping).IsFlags)
{
Writer.Write("(");
string[] values = ((string)defaultValue!).Split(null);
for (int i = 0; i < values.Length; i++)
{
if (string.IsNullOrEmpty(values[i]))
continue;
if (i > 0)
Writer.WriteLine(" | ");
Writer.Write(RaCodeGen.GetStringForEnumCompare((EnumMapping)mapping, values[i], mapping.TypeDesc.UseReflection));
}
Writer.Write(")");
}
else
{
Writer.Write(RaCodeGen.GetStringForEnumCompare((EnumMapping)mapping, (string)defaultValue!, mapping.TypeDesc.UseReflection));
}
Writer.Write(")");
}
else
{
WriteCheckDefault(mapping, source, defaultValue!, isNullable);
}
Writer.WriteLine(" {");
Writer.Indent++;
}
Writer.Write(method);
Writer.Write("(");
WriteQuotedCSharpString(name);
if (ns != null)
{
Writer.Write(", ");
WriteQuotedCSharpString(ns);
}
Writer.Write(", ");
if (mapping is EnumMapping)
{
WriteEnumValue((EnumMapping)mapping, source);
}
else
{
WritePrimitiveValue(typeDesc, source);
}
if (writeXsiType)
{
Writer.Write(", new System.Xml.XmlQualifiedName(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.Write(")");
}
Writer.WriteLine(");");
if (hasDefault)
{
Writer.Indent--;
Writer.WriteLine("}");
}
}
private void WriteTag(string methodName, string name, string? ns)
{
Writer.Write(methodName);
Writer.Write("(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
if (ns == null)
{
Writer.Write("null");
}
else
{
WriteQuotedCSharpString(ns);
}
Writer.WriteLine(");");
}
private void WriteTag(string methodName, string name, string? ns, bool writePrefixed)
{
Writer.Write(methodName);
Writer.Write("(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
if (ns == null)
{
Writer.Write("null");
}
else
{
WriteQuotedCSharpString(ns);
}
Writer.Write(", null, ");
if (writePrefixed)
Writer.Write("true");
else
Writer.Write("false");
Writer.WriteLine(");");
}
private void WriteStartElement(string name, string? ns, bool writePrefixed)
{
WriteTag("WriteStartElement", name, ns, writePrefixed);
}
private void WriteEndElement()
{
Writer.WriteLine("WriteEndElement();");
}
private void WriteEndElement(string source)
{
Writer.Write("WriteEndElement(");
Writer.Write(source);
Writer.WriteLine(");");
}
private void WriteEncodedNullTag(string name, string? ns)
{
WriteTag("WriteNullTagEncoded", name, ns);
}
private void WriteLiteralNullTag(string name, string? ns)
{
WriteTag("WriteNullTagLiteral", name, ns);
}
private void WriteEmptyTag(string name, string? ns)
{
WriteTag("WriteEmptyTag", name, ns);
}
[RequiresUnreferencedCode("calls WriteMember")]
private string GenerateMembersElement(XmlMembersMapping xmlMembersMapping)
{
ElementAccessor element = xmlMembersMapping.Accessor;
MembersMapping mapping = (MembersMapping)element.Mapping!;
bool hasWrapperElement = mapping.HasWrapperElement;
bool writeAccessors = mapping.WriteAccessors;
bool isRpc = xmlMembersMapping.IsSoap && writeAccessors;
string methodName = NextMethodName(element.Name);
Writer.WriteLine();
Writer.Write("public void ");
Writer.Write(methodName);
Writer.WriteLine("(object[] p) {");
Writer.Indent++;
Writer.WriteLine("WriteStartDocument();");
if (!mapping.IsSoap)
{
Writer.WriteLine("TopLevelElement();");
}
// in the top-level method add check for the parameters length,
// because visual basic does not have a concept of an <out> parameter it uses <ByRef> instead
// so sometime we think that we have more parameters then supplied
Writer.WriteLine("int pLength = p.Length;");
if (hasWrapperElement)
{
WriteStartElement(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""), mapping.IsSoap);
int xmlnsMember = FindXmlnsIndex(mapping.Members!);
if (xmlnsMember >= 0)
{
string source = $"(({typeof(System.Xml.Serialization.XmlSerializerNamespaces).FullName})p[{xmlnsMember}])";
Writer.Write("if (pLength > ");
Writer.Write(xmlnsMember.ToString(CultureInfo.InvariantCulture));
Writer.WriteLine(") {");
Writer.Indent++;
WriteNamespaces(source);
Writer.Indent--;
Writer.WriteLine("}");
}
for (int i = 0; i < mapping.Members!.Length; i++)
{
MemberMapping member = mapping.Members[i];
if (member.Attribute != null && !member.Ignore)
{
string index = i.ToString(CultureInfo.InvariantCulture);
string source = $"p[{index}]";
string? specifiedSource = null;
int specifiedPosition = 0;
if (member.CheckSpecified != SpecifiedAccessor.None)
{
string memberNameSpecified = $"{member.Name}Specified";
for (int j = 0; j < mapping.Members.Length; j++)
{
if (mapping.Members[j].Name == memberNameSpecified)
{
specifiedSource = $"((bool) p[{j}])";
specifiedPosition = j;
break;
}
}
}
Writer.Write("if (pLength > ");
Writer.Write(index);
Writer.WriteLine(") {");
Writer.Indent++;
if (specifiedSource != null)
{
Writer.Write("if (pLength <= ");
Writer.Write(specifiedPosition.ToString(CultureInfo.InvariantCulture));
Writer.Write(" || ");
Writer.Write(specifiedSource);
Writer.WriteLine(") {");
Writer.Indent++;
}
WriteMember(source, member.Attribute, member.TypeDesc!, "p");
if (specifiedSource != null)
{
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
}
}
for (int i = 0; i < mapping.Members!.Length; i++)
{
MemberMapping member = mapping.Members[i];
if (member.Xmlns != null)
continue;
if (member.Ignore)
continue;
string? specifiedSource = null;
int specifiedPosition = 0;
if (member.CheckSpecified != SpecifiedAccessor.None)
{
string memberNameSpecified = $"{member.Name}Specified";
for (int j = 0; j < mapping.Members.Length; j++)
{
if (mapping.Members[j].Name == memberNameSpecified)
{
specifiedSource = $"((bool) p[{j}])";
specifiedPosition = j;
break;
}
}
}
string index = i.ToString(CultureInfo.InvariantCulture);
Writer.Write("if (pLength > ");
Writer.Write(index);
Writer.WriteLine(") {");
Writer.Indent++;
if (specifiedSource != null)
{
Writer.Write("if (pLength <= ");
Writer.Write(specifiedPosition.ToString(CultureInfo.InvariantCulture));
Writer.Write(" || ");
Writer.Write(specifiedSource);
Writer.WriteLine(") {");
Writer.Indent++;
}
string source = $"p[{index}]";
string? enumSource = null;
if (member.ChoiceIdentifier != null)
{
for (int j = 0; j < mapping.Members.Length; j++)
{
if (mapping.Members[j].Name == member.ChoiceIdentifier.MemberName)
{
if (member.ChoiceIdentifier.Mapping!.TypeDesc!.UseReflection)
enumSource = $"p[{j}]";
else
enumSource = $"(({mapping.Members[j].TypeDesc!.CSharpName })p[{j}])";
break;
}
}
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (enumSource == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorDetails, "Can not find " + member.ChoiceIdentifier.MemberName + " in the members mapping."));
#endif
}
if (isRpc && member.IsReturnValue && member.Elements!.Length > 0)
{
Writer.Write("WriteRpcResult(");
WriteQuotedCSharpString(member.Elements[0].Name);
Writer.Write(", ");
WriteQuotedCSharpString("");
Writer.WriteLine(");");
}
// override writeAccessors choice when we've written a wrapper element
WriteMember(source, enumSource, member.ElementsSortedByDerivation!, member.Text, member.ChoiceIdentifier, member.TypeDesc!, writeAccessors || hasWrapperElement);
if (specifiedSource != null)
{
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
if (hasWrapperElement)
{
WriteEndElement();
}
if (element.IsSoap)
{
if (!hasWrapperElement && !writeAccessors)
{
// doc/bare case -- allow extra members
Writer.Write("if (pLength > ");
Writer.Write(mapping.Members.Length.ToString(CultureInfo.InvariantCulture));
Writer.WriteLine(") {");
Writer.Indent++;
WriteExtraMembers(mapping.Members.Length.ToString(CultureInfo.InvariantCulture), "pLength");
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.WriteLine("WriteReferencedElements();");
}
Writer.Indent--;
Writer.WriteLine("}");
return methodName;
}
[RequiresUnreferencedCode("calls WriteMember")]
private string GenerateTypeElement(XmlTypeMapping xmlTypeMapping)
{
ElementAccessor element = xmlTypeMapping.Accessor;
TypeMapping mapping = element.Mapping!;
string methodName = NextMethodName(element.Name);
Writer.WriteLine();
Writer.Write("public void ");
Writer.Write(methodName);
Writer.WriteLine("(object o) {");
Writer.Indent++;
Writer.WriteLine("WriteStartDocument();");
Writer.WriteLine("if (o == null) {");
Writer.Indent++;
if (element.IsNullable)
{
if (mapping.IsSoap)
WriteEncodedNullTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""));
else
WriteLiteralNullTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""));
}
else
WriteEmptyTag(element.Name, (element.Form == XmlSchemaForm.Qualified ? element.Namespace : ""));
Writer.WriteLine("return;");
Writer.Indent--;
Writer.WriteLine("}");
if (!mapping.IsSoap && !mapping.TypeDesc!.IsValueType && !mapping.TypeDesc.Type!.IsPrimitive)
{
Writer.WriteLine("TopLevelElement();");
}
WriteMember("o", null, new ElementAccessor[] { element }, null, null, mapping.TypeDesc!, !element.IsSoap);
if (mapping.IsSoap)
{
Writer.WriteLine("WriteReferencedElements();");
}
Writer.Indent--;
Writer.WriteLine("}");
return methodName;
}
private string NextMethodName(string name)
{
return string.Create(CultureInfo.InvariantCulture, $"Write{++NextMethodNumber}_{CodeIdentifier.MakeValidInternal(name)}");
}
private void WriteEnumMethod(EnumMapping mapping)
{
string methodName = (string)MethodNames[mapping]!;
Writer.WriteLine();
string fullTypeName = mapping.TypeDesc!.CSharpName;
if (mapping.IsSoap)
{
Writer.Write("void ");
Writer.Write(methodName);
Writer.WriteLine("(object e) {");
WriteLocalDecl(fullTypeName, "v", "e", mapping.TypeDesc.UseReflection);
}
else
{
Writer.Write("string ");
Writer.Write(methodName);
Writer.Write("(");
Writer.Write(mapping.TypeDesc.UseReflection ? "object" : fullTypeName);
Writer.WriteLine(" v) {");
}
Writer.Indent++;
Writer.WriteLine("string s = null;");
ConstantMapping[] constants = mapping.Constants!;
if (constants.Length > 0)
{
Hashtable values = new Hashtable();
if (mapping.TypeDesc.UseReflection)
Writer.WriteLine($"switch ({ReflectionAwareCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection)} ){{");
else
Writer.WriteLine("switch (v) {");
Writer.Indent++;
for (int i = 0; i < constants.Length; i++)
{
ConstantMapping c = constants[i];
if (values[c.Value] == null)
{
WriteEnumCase(fullTypeName, c, mapping.TypeDesc.UseReflection);
Writer.Write("s = ");
WriteQuotedCSharpString(c.XmlName);
Writer.WriteLine("; break;");
values.Add(c.Value, c.Value);
}
}
if (mapping.IsFlags)
{
Writer.Write("default: s = FromEnum(");
Writer.Write(ReflectionAwareCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection));
Writer.Write(", new string[] {");
Writer.Indent++;
for (int i = 0; i < constants.Length; i++)
{
ConstantMapping c = constants[i];
if (i > 0)
Writer.WriteLine(", ");
WriteQuotedCSharpString(c.XmlName);
}
Writer.Write("}, new ");
Writer.Write(typeof(long).FullName);
Writer.Write("[] {");
for (int i = 0; i < constants.Length; i++)
{
ConstantMapping c = constants[i];
if (i > 0)
Writer.WriteLine(", ");
Writer.Write("(long)");
if (mapping.TypeDesc.UseReflection)
Writer.Write(c.Value.ToString(CultureInfo.InvariantCulture));
else
{
Writer.Write(fullTypeName);
Writer.Write(".@");
CodeIdentifier.CheckValidIdentifier(c.Name);
Writer.Write(c.Name);
}
}
Writer.Indent--;
Writer.Write("}, ");
WriteQuotedCSharpString(mapping.TypeDesc.FullName);
Writer.WriteLine("); break;");
}
else
{
Writer.Write("default: throw CreateInvalidEnumValueException(");
Writer.Write(ReflectionAwareCodeGen.GetStringForEnumLongValue("v", mapping.TypeDesc.UseReflection));
Writer.Write(".ToString(System.Globalization.CultureInfo.InvariantCulture), ");
WriteQuotedCSharpString(mapping.TypeDesc.FullName);
Writer.WriteLine(");");
}
Writer.Indent--;
Writer.WriteLine("}");
}
if (mapping.IsSoap)
{
Writer.Write("WriteXsiType(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.WriteLine(");");
Writer.WriteLine("Writer.WriteString(s);");
}
else
{
Writer.WriteLine("return s;");
}
Writer.Indent--;
Writer.WriteLine("}");
}
private void WriteDerivedTypes(StructMapping mapping)
{
for (StructMapping? derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
{
string fullTypeName = derived.TypeDesc!.CSharpName;
Writer.Write("if (");
WriteTypeCompare("t", fullTypeName, derived.TypeDesc.UseReflection);
Writer.WriteLine(") {");
Writer.Indent++;
string? methodName = ReferenceMapping(derived);
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (methodName == null) throw new InvalidOperationException("derived from " + mapping.TypeDesc!.FullName + ", " + SR.Format(SR.XmlInternalErrorMethod, derived.TypeDesc.Name) + Environment.StackTrace);
#endif
Writer.Write(methodName);
Writer.Write("(n, ns,");
if (!derived.TypeDesc.UseReflection) Writer.Write($"({fullTypeName})");
Writer.Write("o");
if (derived.TypeDesc.IsNullable)
Writer.Write(", isNullable");
Writer.Write(", true");
Writer.WriteLine(");");
Writer.WriteLine("return;");
Writer.Indent--;
Writer.WriteLine("}");
WriteDerivedTypes(derived);
}
}
[RequiresUnreferencedCode("calls WriteMember")]
private void WriteEnumAndArrayTypes()
{
foreach (TypeScope scope in Scopes)
{
foreach (Mapping m in scope.TypeMappings)
{
if (m is EnumMapping && !m.IsSoap)
{
EnumMapping mapping = (EnumMapping)m;
string fullTypeName = mapping.TypeDesc!.CSharpName;
Writer.Write("if (");
WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection);
Writer.WriteLine(") {");
Writer.Indent++;
string? methodName = ReferenceMapping(mapping);
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, mapping.TypeDesc.Name) + Environment.StackTrace);
#endif
Writer.WriteLine("Writer.WriteStartElement(n, ns);");
Writer.Write("WriteXsiType(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.WriteLine(");");
Writer.Write("Writer.WriteString(");
Writer.Write(methodName);
Writer.Write("(");
if (!mapping.TypeDesc.UseReflection) Writer.Write($"({fullTypeName})");
Writer.WriteLine("o));");
Writer.WriteLine("Writer.WriteEndElement();");
Writer.WriteLine("return;");
Writer.Indent--;
Writer.WriteLine("}");
}
else if (m is ArrayMapping && !m.IsSoap)
{
ArrayMapping? mapping = m as ArrayMapping;
if (mapping == null || m.IsSoap) continue;
string fullTypeName = mapping.TypeDesc!.CSharpName;
Writer.Write("if (");
if (mapping.TypeDesc.IsArray)
WriteArrayTypeCompare("t", fullTypeName, mapping.TypeDesc.ArrayElementTypeDesc!.CSharpName, mapping.TypeDesc.UseReflection);
else
WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection);
Writer.WriteLine(") {");
Writer.Indent++;
Writer.WriteLine("Writer.WriteStartElement(n, ns);");
Writer.Write("WriteXsiType(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.WriteLine(");");
WriteMember("o", null, mapping.ElementsSortedByDerivation!, null, null, mapping.TypeDesc, true);
Writer.WriteLine("Writer.WriteEndElement();");
Writer.WriteLine("return;");
Writer.Indent--;
Writer.WriteLine("}");
}
}
}
}
[RequiresUnreferencedCode("calls WriteMember")]
private void WriteStructMethod(StructMapping mapping)
{
if (mapping.IsSoap && mapping.TypeDesc!.IsRoot) return;
string? methodName = (string?)MethodNames[mapping];
Writer.WriteLine();
Writer.Write("void ");
Writer.Write(methodName);
string fullTypeName = mapping.TypeDesc!.CSharpName;
if (mapping.IsSoap)
{
Writer.WriteLine("(object s) {");
Writer.Indent++;
WriteLocalDecl(fullTypeName, "o", "s", mapping.TypeDesc.UseReflection);
}
else
{
Writer.Write("(string n, string ns, ");
Writer.Write(mapping.TypeDesc.UseReflection ? "object" : fullTypeName);
Writer.Write(" o");
if (mapping.TypeDesc.IsNullable)
Writer.Write(", bool isNullable");
Writer.WriteLine(", bool needType) {");
Writer.Indent++;
if (mapping.TypeDesc.IsNullable)
{
Writer.WriteLine("if ((object)o == null) {");
Writer.Indent++;
Writer.WriteLine("if (isNullable) WriteNullTagLiteral(n, ns);");
Writer.WriteLine("return;");
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.WriteLine("if (!needType) {");
Writer.Indent++;
Writer.Write(typeof(Type).FullName);
Writer.WriteLine(" t = o.GetType();");
Writer.Write("if (");
WriteTypeCompare("t", fullTypeName, mapping.TypeDesc.UseReflection);
Writer.WriteLine(") {");
Writer.WriteLine("}");
Writer.WriteLine("else {");
Writer.Indent++;
WriteDerivedTypes(mapping);
if (mapping.TypeDesc.IsRoot)
WriteEnumAndArrayTypes();
if (mapping.TypeDesc.IsRoot)
{
Writer.WriteLine("WriteTypedPrimitive(n, ns, o, true);");
Writer.WriteLine("return;");
}
else
{
Writer.WriteLine("throw CreateUnknownTypeException(o);");
}
Writer.Indent--;
Writer.WriteLine("}");
Writer.Indent--;
Writer.WriteLine("}");
}
if (!mapping.TypeDesc.IsAbstract)
{
if (mapping.TypeDesc.Type != null && typeof(XmlSchemaObject).IsAssignableFrom(mapping.TypeDesc.Type))
{
Writer.WriteLine("EscapeName = false;");
}
string? xmlnsSource = null;
MemberMapping[] members = TypeScope.GetAllMembers(mapping);
int xmlnsMember = FindXmlnsIndex(members);
if (xmlnsMember >= 0)
{
MemberMapping member = members[xmlnsMember];
CodeIdentifier.CheckValidIdentifier(member.Name);
xmlnsSource = RaCodeGen.GetStringForMember("o", member.Name, mapping.TypeDesc);
if (mapping.TypeDesc.UseReflection)
{
xmlnsSource = $"(({member.TypeDesc!.CSharpName}){xmlnsSource})";
}
}
if (!mapping.IsSoap)
{
Writer.Write("WriteStartElement(n, ns, o, false, ");
if (xmlnsSource == null)
Writer.Write("null");
else
Writer.Write(xmlnsSource);
Writer.WriteLine(");");
if (!mapping.TypeDesc.IsRoot)
{
Writer.Write("if (needType) WriteXsiType(");
WriteQuotedCSharpString(mapping.TypeName);
Writer.Write(", ");
WriteQuotedCSharpString(mapping.Namespace);
Writer.WriteLine(");");
}
}
else if (xmlnsSource != null)
{
WriteNamespaces(xmlnsSource);
}
for (int i = 0; i < members.Length; i++)
{
MemberMapping m = members[i];
if (m.Attribute != null)
{
CodeIdentifier.CheckValidIdentifier(m.Name);
if (m.CheckShouldPersist)
{
Writer.Write("if (");
string methodInvoke = RaCodeGen.GetStringForMethodInvoke("o", fullTypeName, $"ShouldSerialize{m.Name}", mapping.TypeDesc.UseReflection);
if (mapping.TypeDesc.UseReflection) methodInvoke = $"(({typeof(bool).FullName}){methodInvoke})";
Writer.Write(methodInvoke);
Writer.WriteLine(") {");
Writer.Indent++;
}
if (m.CheckSpecified != SpecifiedAccessor.None)
{
Writer.Write("if (");
string memberGet = RaCodeGen.GetStringForMember("o", $"{m.Name}Specified", mapping.TypeDesc);
if (mapping.TypeDesc.UseReflection) memberGet = $"(({typeof(bool).FullName}){memberGet})";
Writer.Write(memberGet);
Writer.WriteLine(") {");
Writer.Indent++;
}
WriteMember(RaCodeGen.GetStringForMember("o", m.Name, mapping.TypeDesc), m.Attribute, m.TypeDesc!, "o");
if (m.CheckSpecified != SpecifiedAccessor.None)
{
Writer.Indent--;
Writer.WriteLine("}");
}
if (m.CheckShouldPersist)
{
Writer.Indent--;
Writer.WriteLine("}");
}
}
}
for (int i = 0; i < members.Length; i++)
{
MemberMapping m = members[i];
if (m.Xmlns != null)
continue;
CodeIdentifier.CheckValidIdentifier(m.Name);
bool checkShouldPersist = m.CheckShouldPersist && (m.Elements!.Length > 0 || m.Text != null);
if (checkShouldPersist)
{
Writer.Write("if (");
string methodInvoke = RaCodeGen.GetStringForMethodInvoke("o", fullTypeName, $"ShouldSerialize{m.Name}", mapping.TypeDesc.UseReflection);
if (mapping.TypeDesc.UseReflection) methodInvoke = $"(({typeof(bool).FullName}){methodInvoke})";
Writer.Write(methodInvoke);
Writer.WriteLine(") {");
Writer.Indent++;
}
if (m.CheckSpecified != SpecifiedAccessor.None)
{
Writer.Write("if (");
string memberGet = RaCodeGen.GetStringForMember("o", $"{m.Name}Specified", mapping.TypeDesc);
if (mapping.TypeDesc.UseReflection) memberGet = $"(({typeof(bool).FullName}){memberGet})";
Writer.Write(memberGet);
Writer.WriteLine(") {");
Writer.Indent++;
}
string? choiceSource = null;
if (m.ChoiceIdentifier != null)
{
CodeIdentifier.CheckValidIdentifier(m.ChoiceIdentifier.MemberName);
choiceSource = RaCodeGen.GetStringForMember("o", m.ChoiceIdentifier.MemberName, mapping.TypeDesc);
}
WriteMember(RaCodeGen.GetStringForMember("o", m.Name, mapping.TypeDesc), choiceSource, m.ElementsSortedByDerivation!, m.Text, m.ChoiceIdentifier, m.TypeDesc!, true);
if (m.CheckSpecified != SpecifiedAccessor.None)
{
Writer.Indent--;
Writer.WriteLine("}");
}
if (checkShouldPersist)
{
Writer.Indent--;
Writer.WriteLine("}");
}
}
if (!mapping.IsSoap)
{
WriteEndElement("o");
}
}
Writer.Indent--;
Writer.WriteLine("}");
}
private bool CanOptimizeWriteListSequence(TypeDesc? listElementTypeDesc)
{
// check to see if we can write values of the attribute sequentially
// currently we have only one data type (XmlQualifiedName) that we can not write "inline",
// because we need to output xmlns:qx="..." for each of the qnames
return (listElementTypeDesc != null && listElementTypeDesc != QnameTypeDesc);
}
[RequiresUnreferencedCode("calls WriteAttribute")]
private void WriteMember(string source, AttributeAccessor attribute, TypeDesc memberTypeDesc, string parent)
{
if (memberTypeDesc.IsAbstract) return;
if (memberTypeDesc.IsArrayLike)
{
Writer.WriteLine("{");
Writer.Indent++;
string fullTypeName = memberTypeDesc.CSharpName;
WriteArrayLocalDecl(fullTypeName, "a", source, memberTypeDesc);
if (memberTypeDesc.IsNullable)
{
Writer.WriteLine("if (a != null) {");
Writer.Indent++;
}
if (attribute.IsList)
{
if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
{
Writer.Write("Writer.WriteStartAttribute(null, ");
WriteQuotedCSharpString(attribute.Name);
Writer.Write(", ");
string? ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : string.Empty;
if (ns != null)
{
WriteQuotedCSharpString(ns);
}
else
{
Writer.Write("null");
}
Writer.WriteLine(");");
}
else
{
Writer.Write(typeof(StringBuilder).FullName);
Writer.Write(" sb = new ");
Writer.Write(typeof(StringBuilder).FullName);
Writer.WriteLine("();");
}
}
TypeDesc arrayElementTypeDesc = memberTypeDesc.ArrayElementTypeDesc!;
if (memberTypeDesc.IsEnumerable)
{
Writer.Write(" e = ");
Writer.Write(typeof(IEnumerator).FullName);
if (memberTypeDesc.IsPrivateImplementation)
{
Writer.Write("((");
Writer.Write(typeof(IEnumerable).FullName);
Writer.WriteLine(").GetEnumerator();");
}
else if (memberTypeDesc.IsGenericInterface)
{
if (memberTypeDesc.UseReflection)
{
// we use wildcard method name for generic GetEnumerator method, so we cannot use GetStringForMethodInvoke call here
Writer.Write("(");
Writer.Write(typeof(IEnumerator).FullName);
Writer.Write(")");
Writer.Write(RaCodeGen.GetReflectionVariable(memberTypeDesc.CSharpName, "System.Collections.Generic.IEnumerable*"));
Writer.WriteLine(".Invoke(a, new object[0]);");
}
else
{
Writer.Write("((System.Collections.Generic.IEnumerable<");
Writer.Write(arrayElementTypeDesc.CSharpName);
Writer.WriteLine(">)a).GetEnumerator();");
}
}
else
{
if (memberTypeDesc.UseReflection)
{
Writer.Write("(");
Writer.Write(typeof(IEnumerator).FullName);
Writer.Write(")");
}
Writer.Write(RaCodeGen.GetStringForMethodInvoke("a", memberTypeDesc.CSharpName, "GetEnumerator", memberTypeDesc.UseReflection));
Writer.WriteLine(";");
}
Writer.WriteLine("if (e != null)");
Writer.WriteLine("while (e.MoveNext()) {");
Writer.Indent++;
string arrayTypeFullName = arrayElementTypeDesc.CSharpName;
WriteLocalDecl(arrayTypeFullName, "ai", "e.Current", arrayElementTypeDesc.UseReflection);
}
else
{
Writer.Write("for (int i = 0; i < ");
if (memberTypeDesc.IsArray)
{
Writer.WriteLine("a.Length; i++) {");
}
else
{
Writer.Write("((");
Writer.Write(typeof(ICollection).FullName);
Writer.WriteLine(")a).Count; i++) {");
}
Writer.Indent++;
string arrayTypeFullName = arrayElementTypeDesc.CSharpName;
WriteLocalDecl(arrayTypeFullName, "ai", RaCodeGen.GetStringForArrayMember("a", "i", memberTypeDesc), arrayElementTypeDesc.UseReflection);
}
if (attribute.IsList)
{
// check to see if we can write values of the attribute sequentially
if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
{
Writer.WriteLine("if (i != 0) Writer.WriteString(\" \");");
Writer.Write("WriteValue(");
}
else
{
Writer.WriteLine("if (i != 0) sb.Append(\" \");");
Writer.Write("sb.Append(");
}
if (attribute.Mapping is EnumMapping)
WriteEnumValue((EnumMapping)attribute.Mapping, "ai");
else
WritePrimitiveValue(arrayElementTypeDesc, "ai");
Writer.WriteLine(");");
}
else
{
WriteAttribute("ai", attribute, parent);
}
Writer.Indent--;
Writer.WriteLine("}");
if (attribute.IsList)
{
// check to see if we can write values of the attribute sequentially
if (CanOptimizeWriteListSequence(memberTypeDesc.ArrayElementTypeDesc))
{
Writer.WriteLine("Writer.WriteEndAttribute();");
}
else
{
Writer.WriteLine("if (sb.Length != 0) {");
Writer.Indent++;
Writer.Write("WriteAttribute(");
WriteQuotedCSharpString(attribute.Name);
Writer.Write(", ");
string? ns = attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : string.Empty;
if (ns != null)
{
WriteQuotedCSharpString(ns);
Writer.Write(", ");
}
Writer.WriteLine("sb.ToString());");
Writer.Indent--;
Writer.WriteLine("}");
}
}
if (memberTypeDesc.IsNullable)
{
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
else
{
WriteAttribute(source, attribute, parent);
}
}
[RequiresUnreferencedCode("calls WritePrimitive")]
private void WriteAttribute(string source, AttributeAccessor attribute, string parent)
{
if (attribute.Mapping is SpecialMapping special)
{
if (special.TypeDesc!.Kind == TypeKind.Attribute || special.TypeDesc.CanBeAttributeValue)
{
Writer.Write("WriteXmlAttribute(");
Writer.Write(source);
Writer.Write(", ");
Writer.Write(parent);
Writer.WriteLine(");");
}
else
throw new InvalidOperationException(SR.XmlInternalError);
}
else
{
TypeDesc typeDesc = attribute.Mapping!.TypeDesc!;
if (!typeDesc.UseReflection) source = $"(({typeDesc.CSharpName}){source})";
WritePrimitive("WriteAttribute", attribute.Name, attribute.Form == XmlSchemaForm.Qualified ? attribute.Namespace : "", attribute.Default, source, attribute.Mapping, false, false, false);
}
}
[RequiresUnreferencedCode("calls WriteElements")]
private void WriteMember(string source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc memberTypeDesc, bool writeAccessors)
{
if (memberTypeDesc.IsArrayLike &&
!(elements.Length == 1 && elements[0].Mapping is ArrayMapping))
WriteArray(source, choiceSource, elements, text, choice, memberTypeDesc);
else
WriteElements(source, choiceSource, elements, text, choice, "a", writeAccessors, memberTypeDesc.IsNullable);
}
[RequiresUnreferencedCode("calls WriteArrayItems")]
private void WriteArray(string source, string? choiceSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc)
{
if (elements.Length == 0 && text == null) return;
Writer.WriteLine("{");
Writer.Indent++;
string arrayTypeName = arrayTypeDesc.CSharpName;
WriteArrayLocalDecl(arrayTypeName, "a", source, arrayTypeDesc);
if (arrayTypeDesc.IsNullable)
{
Writer.WriteLine("if (a != null) {");
Writer.Indent++;
}
if (choice != null)
{
string choiceFullName = choice.Mapping!.TypeDesc!.CSharpName;
WriteArrayLocalDecl($"{choiceFullName}[]", "c", choiceSource, choice.Mapping.TypeDesc);
// write check for the choice identifier array
Writer.WriteLine("if (c == null || c.Length < a.Length) {");
Writer.Indent++;
Writer.Write("throw CreateInvalidChoiceIdentifierValueException(");
WriteQuotedCSharpString(choice.Mapping.TypeDesc.FullName);
Writer.Write(", ");
WriteQuotedCSharpString(choice.MemberName);
Writer.Write(");");
Writer.Indent--;
Writer.WriteLine("}");
}
WriteArrayItems(elements, text, choice, arrayTypeDesc, "a", "c");
if (arrayTypeDesc.IsNullable)
{
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
[RequiresUnreferencedCode("calls WriteElements")]
private void WriteArrayItems(ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, TypeDesc arrayTypeDesc, string arrayName, string? choiceName)
{
TypeDesc arrayElementTypeDesc = arrayTypeDesc.ArrayElementTypeDesc!;
if (arrayTypeDesc.IsEnumerable)
{
Writer.Write(typeof(IEnumerator).FullName);
Writer.Write(" e = ");
if (arrayTypeDesc.IsPrivateImplementation)
{
Writer.Write("((");
Writer.Write(typeof(IEnumerable).FullName);
Writer.Write(")");
Writer.Write(arrayName);
Writer.WriteLine(").GetEnumerator();");
}
else if (arrayTypeDesc.IsGenericInterface)
{
if (arrayTypeDesc.UseReflection)
{
// we use wildcard method name for generic GetEnumerator method, so we cannot use GetStringForMethodInvoke call here
Writer.Write("(");
Writer.Write(typeof(IEnumerator).FullName);
Writer.Write(")");
Writer.Write(RaCodeGen.GetReflectionVariable(arrayTypeDesc.CSharpName, "System.Collections.Generic.IEnumerable*"));
Writer.Write(".Invoke(");
Writer.Write(arrayName);
Writer.WriteLine(", new object[0]);");
}
else
{
Writer.Write("((System.Collections.Generic.IEnumerable<");
Writer.Write(arrayElementTypeDesc.CSharpName);
Writer.Write(">)");
Writer.Write(arrayName);
Writer.WriteLine(").GetEnumerator();");
}
}
else
{
if (arrayTypeDesc.UseReflection)
{
Writer.Write("(");
Writer.Write(typeof(IEnumerator).FullName);
Writer.Write(")");
}
Writer.Write(RaCodeGen.GetStringForMethodInvoke(arrayName, arrayTypeDesc.CSharpName, "GetEnumerator", arrayTypeDesc.UseReflection));
Writer.WriteLine(";");
}
Writer.WriteLine("if (e != null)");
Writer.WriteLine("while (e.MoveNext()) {");
Writer.Indent++;
string arrayTypeFullName = arrayElementTypeDesc.CSharpName;
WriteLocalDecl(arrayTypeFullName, $"{arrayName}i", "e.Current", arrayElementTypeDesc.UseReflection);
WriteElements($"{arrayName}i", $"{choiceName}i", elements, text, choice, $"{arrayName}a", true, true);
}
else
{
Writer.Write("for (int i");
Writer.Write(arrayName);
Writer.Write(" = 0; i");
Writer.Write(arrayName);
Writer.Write(" < ");
if (arrayTypeDesc.IsArray)
{
Writer.Write(arrayName);
Writer.Write(".Length");
}
else
{
Writer.Write("((");
Writer.Write(typeof(ICollection).FullName);
Writer.Write(")");
Writer.Write(arrayName);
Writer.Write(").Count");
}
Writer.Write("; i");
Writer.Write(arrayName);
Writer.WriteLine("++) {");
Writer.Indent++;
int count = elements.Length + (text == null ? 0 : 1);
if (count > 1)
{
string arrayTypeFullName = arrayElementTypeDesc.CSharpName;
WriteLocalDecl(arrayTypeFullName, $"{arrayName}i", RaCodeGen.GetStringForArrayMember(arrayName, $"i{arrayName}", arrayTypeDesc), arrayElementTypeDesc.UseReflection);
if (choice != null)
{
string choiceFullName = choice.Mapping!.TypeDesc!.CSharpName;
WriteLocalDecl(choiceFullName, $"{choiceName}i", RaCodeGen.GetStringForArrayMember(choiceName!, $"i{arrayName}", choice.Mapping.TypeDesc), choice.Mapping.TypeDesc.UseReflection);
}
WriteElements($"{arrayName}i", $"{choiceName}i", elements, text, choice, $"{arrayName}a", true, arrayElementTypeDesc.IsNullable);
}
else
{
WriteElements(RaCodeGen.GetStringForArrayMember(arrayName, $"i{arrayName}", arrayTypeDesc), elements, text, choice, $"{arrayName}a", true, arrayElementTypeDesc.IsNullable);
}
}
Writer.Indent--;
Writer.WriteLine("}");
}
[RequiresUnreferencedCode("calls WriteElements")]
private void WriteElements(string source, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable)
{
WriteElements(source, null, elements, text, choice, arrayName, writeAccessors, isNullable);
}
[RequiresUnreferencedCode("calls WriteElement")]
private void WriteElements(string source, string? enumSource, ElementAccessor[] elements, TextAccessor? text, ChoiceIdentifierAccessor? choice, string arrayName, bool writeAccessors, bool isNullable)
{
if (elements.Length == 0 && text == null) return;
if (elements.Length == 1 && text == null)
{
TypeDesc td = elements[0].IsUnbounded ? elements[0].Mapping!.TypeDesc!.CreateArrayTypeDesc() : elements[0].Mapping!.TypeDesc!;
if (!elements[0].Any && !elements[0].Mapping!.TypeDesc!.UseReflection && !elements[0].Mapping!.TypeDesc!.IsOptionalValue)
source = $"(({td.CSharpName}){source})";
WriteElement(source, elements[0], arrayName, writeAccessors);
}
else
{
if (isNullable && choice == null)
{
Writer.Write("if ((object)(");
Writer.Write(source);
Writer.Write(") != null)");
}
Writer.WriteLine("{");
Writer.Indent++;
int anyCount = 0;
ArrayList namedAnys = new ArrayList();
ElementAccessor? unnamedAny = null; // can only have one
bool wroteFirstIf = false;
string? enumTypeName = choice == null ? null : choice.Mapping!.TypeDesc!.FullName;
for (int i = 0; i < elements.Length; i++)
{
ElementAccessor element = elements[i];
if (element.Any)
{
anyCount++;
if (element.Name != null && element.Name.Length > 0)
{
namedAnys.Add(element);
}
else
{
unnamedAny ??= element;
}
}
else if (choice != null)
{
bool useReflection = element.Mapping!.TypeDesc!.UseReflection;
string fullTypeName = element.Mapping.TypeDesc.CSharpName;
bool enumUseReflection = choice.Mapping!.TypeDesc!.UseReflection;
string enumFullName = (enumUseReflection ? "" : $"{enumTypeName}.@") + FindChoiceEnumValue(element, (EnumMapping)choice.Mapping, enumUseReflection);
if (wroteFirstIf) Writer.Write("else ");
else wroteFirstIf = true;
Writer.Write("if (");
Writer.Write(enumUseReflection ? ReflectionAwareCodeGen.GetStringForEnumLongValue(enumSource!, enumUseReflection) : enumSource);
Writer.Write(" == ");
Writer.Write(enumFullName);
if (isNullable && !element.IsNullable)
{
Writer.Write(" && ((object)(");
Writer.Write(source);
Writer.Write(") != null)");
}
Writer.WriteLine(") {");
Writer.Indent++;
WriteChoiceTypeCheck(source, fullTypeName, useReflection, choice, enumFullName, element.Mapping.TypeDesc);
string castedSource = source;
if (!useReflection)
castedSource = $"(({fullTypeName}){source})";
WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors);
Writer.Indent--;
Writer.WriteLine("}");
}
else
{
bool useReflection = element.Mapping!.TypeDesc!.UseReflection;
TypeDesc td = element.IsUnbounded ? element.Mapping.TypeDesc.CreateArrayTypeDesc() : element.Mapping.TypeDesc;
string fullTypeName = td.CSharpName;
if (wroteFirstIf) Writer.Write("else ");
else wroteFirstIf = true;
Writer.Write("if (");
WriteInstanceOf(source, fullTypeName, useReflection);
Writer.WriteLine(") {");
Writer.Indent++;
string castedSource = source;
if (!useReflection)
castedSource = $"(({fullTypeName}){source})";
WriteElement(element.Any ? source : castedSource, element, arrayName, writeAccessors);
Writer.Indent--;
Writer.WriteLine("}");
}
}
if (anyCount > 0)
{
if (elements.Length - anyCount > 0) Writer.Write("else ");
string fullTypeName = typeof(XmlElement).FullName!;
Writer.Write("if (");
Writer.Write(source);
Writer.Write(" is ");
Writer.Write(fullTypeName);
Writer.WriteLine(") {");
Writer.Indent++;
Writer.Write(fullTypeName);
Writer.Write(" elem = (");
Writer.Write(fullTypeName);
Writer.Write(")");
Writer.Write(source);
Writer.WriteLine(";");
int c = 0;
foreach (ElementAccessor element in namedAnys)
{
if (c++ > 0) Writer.Write("else ");
string? enumFullName = null;
if (choice != null)
{
bool enumUseReflection = choice.Mapping!.TypeDesc!.UseReflection;
enumFullName = (enumUseReflection ? "" : $"{enumTypeName}.@") + FindChoiceEnumValue(element, (EnumMapping)choice.Mapping, enumUseReflection);
Writer.Write("if (");
Writer.Write(enumUseReflection ? ReflectionAwareCodeGen.GetStringForEnumLongValue(enumSource!, enumUseReflection) : enumSource);
Writer.Write(" == ");
Writer.Write(enumFullName);
if (isNullable && !element.IsNullable)
{
Writer.Write(" && ((object)(");
Writer.Write(source);
Writer.Write(") != null)");
}
Writer.WriteLine(") {");
Writer.Indent++;
}
Writer.Write("if (elem.Name == ");
WriteQuotedCSharpString(element.Name);
Writer.Write(" && elem.NamespaceURI == ");
WriteQuotedCSharpString(element.Namespace);
Writer.WriteLine(") {");
Writer.Indent++;
WriteElement("elem", element, arrayName, writeAccessors);
if (choice != null)
{
Writer.Indent--;
Writer.WriteLine("}");
Writer.WriteLine("else {");
Writer.Indent++;
Writer.WriteLine("// throw Value '{0}' of the choice identifier '{1}' does not match element '{2}' from namespace '{3}'.");
Writer.Write("throw CreateChoiceIdentifierValueException(");
WriteQuotedCSharpString(enumFullName);
Writer.Write(", ");
WriteQuotedCSharpString(choice.MemberName);
Writer.WriteLine(", elem.Name, elem.NamespaceURI);");
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
if (c > 0)
{
Writer.WriteLine("else {");
Writer.Indent++;
}
if (unnamedAny != null)
{
WriteElement("elem", unnamedAny, arrayName, writeAccessors);
}
else
{
Writer.WriteLine("throw CreateUnknownAnyElementException(elem.Name, elem.NamespaceURI);");
}
if (c > 0)
{
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
if (text != null)
{
bool useReflection = text.Mapping!.TypeDesc!.UseReflection;
string fullTypeName = text.Mapping.TypeDesc.CSharpName;
if (elements.Length > 0)
{
Writer.Write("else ");
Writer.Write("if (");
WriteInstanceOf(source, fullTypeName, useReflection);
Writer.WriteLine(") {");
Writer.Indent++;
string castedSource = source;
if (!useReflection)
castedSource = $"(({fullTypeName}){source})";
WriteText(castedSource, text);
Writer.Indent--;
Writer.WriteLine("}");
}
else
{
string castedSource = source;
if (!useReflection)
castedSource = $"(({fullTypeName}){source})";
WriteText(castedSource, text);
}
}
if (elements.Length > 0)
{
Writer.Write("else ");
if (isNullable)
{
Writer.Write(" if ((object)(");
Writer.Write(source);
Writer.Write(") != null)");
}
Writer.WriteLine("{");
Writer.Indent++;
Writer.Write("throw CreateUnknownTypeException(");
Writer.Write(source);
Writer.WriteLine(");");
Writer.Indent--;
Writer.WriteLine("}");
}
Writer.Indent--;
Writer.WriteLine("}");
}
}
private void WriteText(string source, TextAccessor text)
{
if (text.Mapping is PrimitiveMapping primitive)
{
Writer.Write("WriteValue(");
if (text.Mapping is EnumMapping enumMapping)
{
WriteEnumValue(enumMapping, source);
}
else
{
WritePrimitiveValue(primitive.TypeDesc!, source);
}
Writer.WriteLine(");");
}
else if (text.Mapping is SpecialMapping simple)
{
switch (simple.TypeDesc!.Kind)
{
case TypeKind.Node:
Writer.Write(source);
Writer.WriteLine(".WriteTo(Writer);");
break;
default:
throw new InvalidOperationException(SR.XmlInternalError);
}
}
}
[RequiresUnreferencedCode("calls WritePrimitive")]
private void WriteElement(string source, ElementAccessor element, string arrayName, bool writeAccessor)
{
string name = writeAccessor ? element.Name : element.Mapping!.TypeName!;
string? ns = element.Any && element.Name.Length == 0 ? null : (element.Form == XmlSchemaForm.Qualified ? (writeAccessor ? element.Namespace : element.Mapping!.Namespace) : "");
if (element.Mapping is NullableMapping)
{
Writer.Write("if (");
Writer.Write(source);
Writer.WriteLine(" != null) {");
Writer.Indent++;
string fullTypeName = element.Mapping.TypeDesc!.BaseTypeDesc!.CSharpName;
string castedSource = source;
if (!element.Mapping.TypeDesc.BaseTypeDesc.UseReflection)
castedSource = $"(({fullTypeName}){source})";
ElementAccessor e = element.Clone();
e.Mapping = ((NullableMapping)element.Mapping).BaseMapping;
WriteElement(e.Any ? source : castedSource, e, arrayName, writeAccessor);
Writer.Indent--;
Writer.WriteLine("}");
if (element.IsNullable)
{
Writer.WriteLine("else {");
Writer.Indent++;
WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : "");
Writer.Indent--;
Writer.WriteLine("}");
}
}
else if (element.Mapping is ArrayMapping arrayMapping)
{
if (arrayMapping.IsSoap)
{
Writer.Write("WritePotentiallyReferencingElement(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
WriteQuotedCSharpString(ns);
Writer.Write(", ");
Writer.Write(source);
if (!writeAccessor)
{
Writer.Write(", ");
Writer.Write(RaCodeGen.GetStringForTypeof(arrayMapping.TypeDesc!.CSharpName, arrayMapping.TypeDesc.UseReflection));
Writer.Write(", true, ");
}
else
{
Writer.Write(", null, false, ");
}
WriteValue(element.IsNullable);
Writer.WriteLine(");");
}
else if (element.IsUnbounded)
{
TypeDesc td = arrayMapping.TypeDesc!.CreateArrayTypeDesc();
string fullTypeName = td.CSharpName;
string elementArrayName = $"el{arrayName}";
string arrayIndex = $"c{elementArrayName}";
Writer.WriteLine("{");
Writer.Indent++;
WriteArrayLocalDecl(fullTypeName, elementArrayName, source, arrayMapping.TypeDesc);
if (element.IsNullable)
{
WriteNullCheckBegin(elementArrayName, element);
}
else
{
if (arrayMapping.TypeDesc.IsNullable)
{
Writer.Write("if (");
Writer.Write(elementArrayName);
Writer.Write(" != null)");
}
Writer.WriteLine("{");
Writer.Indent++;
}
Writer.Write("for (int ");
Writer.Write(arrayIndex);
Writer.Write(" = 0; ");
Writer.Write(arrayIndex);
Writer.Write(" < ");
if (td.IsArray)
{
Writer.Write(elementArrayName);
Writer.Write(".Length");
}
else
{
Writer.Write("((");
Writer.Write(typeof(ICollection).FullName);
Writer.Write(")");
Writer.Write(elementArrayName);
Writer.Write(").Count");
}
Writer.Write("; ");
Writer.Write(arrayIndex);
Writer.WriteLine("++) {");
Writer.Indent++;
element.IsUnbounded = false;
WriteElement($"{elementArrayName}[{arrayIndex}]", element, arrayName, writeAccessor);
element.IsUnbounded = true;
Writer.Indent--;
Writer.WriteLine("}");
Writer.Indent--;
Writer.WriteLine("}");
Writer.Indent--;
Writer.WriteLine("}");
}
else
{
string fullTypeName = arrayMapping.TypeDesc!.CSharpName;
Writer.WriteLine("{");
Writer.Indent++;
WriteArrayLocalDecl(fullTypeName, arrayName, source, arrayMapping.TypeDesc);
if (element.IsNullable)
{
WriteNullCheckBegin(arrayName, element);
}
else
{
if (arrayMapping.TypeDesc.IsNullable)
{
Writer.Write("if (");
Writer.Write(arrayName);
Writer.Write(" != null)");
}
Writer.WriteLine("{");
Writer.Indent++;
}
WriteStartElement(name, ns, false);
WriteArrayItems(arrayMapping.ElementsSortedByDerivation!, null, null, arrayMapping.TypeDesc, arrayName, null);
WriteEndElement();
Writer.Indent--;
Writer.WriteLine("}");
Writer.Indent--;
Writer.WriteLine("}");
}
}
else if (element.Mapping is EnumMapping)
{
if (element.Mapping.IsSoap)
{
string methodName = (string)MethodNames[element.Mapping]!;
Writer.Write("Writer.WriteStartElement(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
WriteQuotedCSharpString(ns);
Writer.WriteLine(");");
Writer.Write(methodName);
Writer.Write("(");
Writer.Write(source);
Writer.WriteLine(");");
WriteEndElement();
}
else
{
WritePrimitive("WriteElementString", name, ns, element.Default, source, element.Mapping, false, true, element.IsNullable);
}
}
else if (element.Mapping is PrimitiveMapping primitiveMapping)
{
if (primitiveMapping.TypeDesc == QnameTypeDesc)
WriteQualifiedNameElement(name, ns, element.Default, source, element.IsNullable, primitiveMapping.IsSoap, primitiveMapping);
else
{
string suffixNullable = primitiveMapping.IsSoap ? "Encoded" : "Literal";
string suffixRaw = primitiveMapping.TypeDesc!.XmlEncodingNotRequired ? "Raw" : "";
WritePrimitive(element.IsNullable ? ("WriteNullableString" + suffixNullable + suffixRaw) : ("WriteElementString" + suffixRaw),
name, ns, element.Default, source, primitiveMapping, primitiveMapping.IsSoap, true, element.IsNullable);
}
}
else if (element.Mapping is StructMapping structMapping)
{
if (structMapping.IsSoap)
{
Writer.Write("WritePotentiallyReferencingElement(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
WriteQuotedCSharpString(ns);
Writer.Write(", ");
Writer.Write(source);
if (!writeAccessor)
{
Writer.Write(", ");
Writer.Write(RaCodeGen.GetStringForTypeof(structMapping.TypeDesc!.CSharpName, structMapping.TypeDesc.UseReflection));
Writer.Write(", true, ");
}
else
{
Writer.Write(", null, false, ");
}
WriteValue(element.IsNullable);
}
else
{
string? methodName = ReferenceMapping(structMapping);
#if DEBUG
// use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
if (methodName == null) throw new InvalidOperationException(SR.Format(SR.XmlInternalErrorMethod, structMapping.TypeDesc!.Name) + Environment.StackTrace);
#endif
Writer.Write(methodName);
Writer.Write("(");
WriteQuotedCSharpString(name);
Writer.Write(", ");
if (ns == null)
Writer.Write("null");
else
{
WriteQuotedCSharpString(ns);
}
Writer.Write(", ");
Writer.Write(source);
if (structMapping.TypeDesc!.IsNullable)
{
Writer.Write(", ");
WriteValue(element.IsNullable);
}
Writer.Write(", false");
}
Writer.WriteLine(");");
}
else if (element.Mapping is SpecialMapping)
{
if (element.Mapping is SerializableMapping)
{
WriteElementCall("WriteSerializable", typeof(IXmlSerializable), source, name, ns, element.IsNullable, !element.Any);
}
else
{
// XmlNode, XmlElement
Writer.Write("if ((");
Writer.Write(source);
Writer.Write(") is ");
Writer.Write(typeof(XmlNode).FullName);
Writer.Write(" || ");
Writer.Write(source);
Writer.Write(" == null");
Writer.WriteLine(") {");
Writer.Indent++;
WriteElementCall("WriteElementLiteral", typeof(XmlNode), source, name, ns, element.IsNullable, element.Any);
Writer.Indent--;
Writer.WriteLine("}");
Writer.WriteLine("else {");
Writer.Indent++;
Writer.Write("throw CreateInvalidAnyTypeException(");
Writer.Write(source);
Writer.WriteLine(");");
Writer.Indent--;
Writer.WriteLine("}");
}
}
else
{
throw new InvalidOperationException(SR.XmlInternalError);
}
}
private void WriteElementCall(string func, Type cast, string source, string name, string? ns, bool isNullable, bool isAny)
{
Writer.Write(func);
Writer.Write("((");
Writer.Write(cast.FullName);
Writer.Write(")");
Writer.Write(source);
Writer.Write(", ");
WriteQuotedCSharpString(name);
Writer.Write(", ");
WriteQuotedCSharpString(ns);
Writer.Write(", ");
WriteValue(isNullable);
Writer.Write(", ");
WriteValue(isAny);
Writer.WriteLine(");");
}
[RequiresUnreferencedCode("calls GetType")]
private void WriteCheckDefault(TypeMapping mapping, string source, object value, bool isNullable)
{
Writer.Write("if (");
if (value is string && ((string)value).Length == 0)
{
// special case for string compare
Writer.Write("(");
Writer.Write(source);
if (isNullable)
Writer.Write(" == null) || (");
else
Writer.Write(" != null) && (");
Writer.Write(source);
Writer.Write(".Length != 0)");
}
else if (value is double || value is float)
{
Writer.Write("!");
Writer.Write(source);
Writer.Write(".Equals(");
Type? type = Type.GetType(mapping.TypeDesc!.Type!.FullName!);
WriteValue(type != null ? Convert.ChangeType(value, type) : value);
Writer.Write(")");
}
else
{
Writer.Write(source);
Writer.Write(" != ");
Type? type = Type.GetType(mapping.TypeDesc!.Type!.FullName!);
WriteValue(type != null ? Convert.ChangeType(value, type) : value);
}
Writer.Write(")");
}
private void WriteChoiceTypeCheck(string source, string fullTypeName, bool useReflection, ChoiceIdentifierAccessor choice, string enumName, TypeDesc typeDesc)
{
Writer.Write("if (((object)");
Writer.Write(source);
Writer.Write(") != null && !(");
WriteInstanceOf(source, fullTypeName, useReflection);
Writer.Write(")) throw CreateMismatchChoiceException(");
WriteQuotedCSharpString(typeDesc.FullName);
Writer.Write(", ");
WriteQuotedCSharpString(choice.MemberName);
Writer.Write(", ");
WriteQuotedCSharpString(enumName);
Writer.WriteLine(");");
}
private void WriteNullCheckBegin(string source, ElementAccessor element)
{
Writer.Write("if ((object)(");
Writer.Write(source);
Writer.WriteLine(") == null) {");
Writer.Indent++;
WriteLiteralNullTag(element.Name, element.Form == XmlSchemaForm.Qualified ? element.Namespace : "");
Writer.Indent--;
Writer.WriteLine("}");
Writer.WriteLine("else {");
Writer.Indent++;
}
private void WriteValue(object value)
{
if (value == null)
{
Writer.Write("null");
}
else
{
Type type = value.GetType();
if (type == typeof(string))
{
string s = (string)value;
WriteQuotedCSharpString(s);
}
else if (type == typeof(char))
{
Writer.Write('\'');
char ch = (char)value;
if (ch == '\'')
Writer.Write("\'");
else
Writer.Write(ch);
Writer.Write('\'');
}
else if (type == typeof(int))
Writer.Write(((int)value).ToString(null, NumberFormatInfo.InvariantInfo));
else if (type == typeof(double))
{
if (double.IsNaN((double)value))
{
Writer.Write("System.Double.NaN");
}
else if (double.IsPositiveInfinity((double)value))
{
Writer.Write("System.Double.PositiveInfinity");
}
else if (double.IsNegativeInfinity((double)value))
{
Writer.Write("System.Double.NegativeInfinity");
}
else
{
Writer.Write(((double)value).ToString("R", NumberFormatInfo.InvariantInfo));
}
}
else if (type == typeof(bool))
Writer.Write((bool)value ? "true" : "false");
else if ((type == typeof(short)) || (type == typeof(long)) || (type == typeof(ushort)) || (type == typeof(uint)) || (type == typeof(ulong)) || (type == typeof(byte)) || (type == typeof(sbyte)))
{
Writer.Write("(");
Writer.Write(type.FullName);
Writer.Write(")");
Writer.Write("(");
Writer.Write(Convert.ToString(value, NumberFormatInfo.InvariantInfo));
Writer.Write(")");
}
else if (type == typeof(float))
{
if (float.IsNaN((float)value))
{
Writer.Write("System.Single.NaN");
}
else if (float.IsPositiveInfinity((float)value))
{
Writer.Write("System.Single.PositiveInfinity");
}
else if (float.IsNegativeInfinity((float)value))
{
Writer.Write("System.Single.NegativeInfinity");
}
else
{
Writer.Write(((float)value).ToString("R", NumberFormatInfo.InvariantInfo));
Writer.Write("f");
}
}
else if (type == typeof(decimal))
{
Writer.Write(((decimal)value).ToString(null, NumberFormatInfo.InvariantInfo));
Writer.Write("m");
}
else if (type == typeof(DateTime))
{
Writer.Write(" new ");
Writer.Write(type.FullName);
Writer.Write("(");
Writer.Write(((DateTime)value).Ticks.ToString(CultureInfo.InvariantCulture));
Writer.Write(")");
}
else if (type == typeof(DateTimeOffset))
{
Writer.Write(" new ");
Writer.Write(type.FullName);
Writer.Write("(");
Writer.Write(((DateTimeOffset)value).Ticks.ToString(CultureInfo.InvariantCulture));
Writer.Write(", new ");
Writer.Write(((DateTimeOffset)value).Offset.GetType().FullName);
Writer.Write("(");
Writer.Write(((DateTimeOffset)value).Offset.Ticks.ToString(CultureInfo.InvariantCulture));
Writer.Write("))");
}
else if (type == typeof(TimeSpan))
{
Writer.Write(" new ");
Writer.Write(type.FullName);
Writer.Write("(");
Writer.Write(((TimeSpan)value).Ticks.ToString(CultureInfo.InvariantCulture));
Writer.Write(")");
}
else
{
if (type.IsEnum)
{
Writer.Write(((int)value).ToString(null, NumberFormatInfo.InvariantInfo));
}
else
{
throw new InvalidOperationException(SR.Format(SR.XmlUnsupportedDefaultType, type.FullName));
}
}
}
}
private void WriteNamespaces(string source)
{
Writer.Write("WriteNamespaceDeclarations(");
Writer.Write(source);
Writer.WriteLine(");");
}
private static int FindXmlnsIndex(MemberMapping[] members)
{
for (int i = 0; i < members.Length; i++)
{
if (members[i].Xmlns == null)
continue;
return i;
}
return -1;
}
private void WriteExtraMembers(string loopStartSource, string loopEndSource)
{
Writer.Write("for (int i = ");
Writer.Write(loopStartSource);
Writer.Write("; i < ");
Writer.Write(loopEndSource);
Writer.WriteLine("; i++) {");
Writer.Indent++;
Writer.WriteLine("if (p[i] != null) {");
Writer.Indent++;
Writer.WriteLine("WritePotentiallyReferencingElement(null, null, p[i], p[i].GetType(), true, false);");
Writer.Indent--;
Writer.WriteLine("}");
Writer.Indent--;
Writer.WriteLine("}");
}
private void WriteLocalDecl(string typeName, string variableName, string initValue, bool useReflection)
{
RaCodeGen.WriteLocalDecl(typeName, variableName, initValue, useReflection);
}
private void WriteArrayLocalDecl(string typeName, string variableName, string? initValue, TypeDesc arrayTypeDesc)
{
RaCodeGen.WriteArrayLocalDecl(typeName, variableName, initValue, arrayTypeDesc);
}
private void WriteTypeCompare(string variable, string escapedTypeName, bool useReflection)
{
RaCodeGen.WriteTypeCompare(variable, escapedTypeName, useReflection);
}
private void WriteInstanceOf(string source, string escapedTypeName, bool useReflection)
{
RaCodeGen.WriteInstanceOf(source, escapedTypeName, useReflection);
}
private void WriteArrayTypeCompare(string variable, string escapedTypeName, string elementTypeName, bool useReflection)
{
RaCodeGen.WriteArrayTypeCompare(variable, escapedTypeName, elementTypeName, useReflection);
}
private void WriteEnumCase(string fullTypeName, ConstantMapping c, bool useReflection)
{
RaCodeGen.WriteEnumCase(fullTypeName, c, useReflection);
}
private static string FindChoiceEnumValue(ElementAccessor element, EnumMapping choiceMapping, bool useReflection)
{
string? enumValue = null;
for (int i = 0; i < choiceMapping.Constants!.Length; i++)
{
string xmlName = choiceMapping.Constants[i].XmlName;
if (element.Any && element.Name.Length == 0)
{
if (xmlName == "##any:")
{
if (useReflection)
enumValue = choiceMapping.Constants[i].Value.ToString(CultureInfo.InvariantCulture);
else
enumValue = choiceMapping.Constants[i].Name;
break;
}
continue;
}
int colon = xmlName.LastIndexOf(':');
ReadOnlySpan<char> choiceNs = colon < 0 ? choiceMapping.Namespace : xmlName.AsSpan(0, colon);
ReadOnlySpan<char> choiceName = colon < 0 ? xmlName : xmlName.AsSpan(colon + 1);
if (choiceName.SequenceEqual(element.Name))
{
if ((element.Form == XmlSchemaForm.Unqualified && choiceNs.IsEmpty) || choiceNs.SequenceEqual(element.Namespace))
{
if (useReflection)
enumValue = choiceMapping.Constants[i].Value.ToString(CultureInfo.InvariantCulture);
else
enumValue = choiceMapping.Constants[i].Name;
break;
}
}
}
if (string.IsNullOrEmpty(enumValue))
{
if (element.Any && element.Name.Length == 0)
{
// Type {0} is missing enumeration value '##any' for XmlAnyElementAttribute.
throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingAnyValue, choiceMapping.TypeDesc!.FullName));
}
// Type {0} is missing value for '{1}'.
throw new InvalidOperationException(SR.Format(SR.XmlChoiceMissingValue, choiceMapping.TypeDesc!.FullName, $"{element.Namespace}:{element.Name}", element.Name, element.Namespace));
}
if (!useReflection)
CodeIdentifier.CheckValidIdentifier(enumValue);
return enumValue;
}
}
}
|