1 // created on 09/04/2003 at 18:58
3 // System.Runtime.Serialization.Formatters.Soap.SoapTypeMapper
6 // Jean-Marc Andre (jean-marc.andre@polymtl.ca)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Reflection;
32 using System.Collections;
33 using System.Runtime.Remoting;
35 using System.Xml.Serialization;
36 using System.Runtime.Serialization.Formatters;
37 using System.Xml.Schema;
38 using System.Runtime.Remoting.Metadata.W3cXsd2001;
39 using System.Globalization;
42 namespace System.Runtime.Serialization.Formatters.Soap {
44 internal class Element
46 private string _prefix;
47 private string _localName;
48 private string _namespaceURI;
49 private MethodInfo _parseMethod;
51 public Element(string prefix, string localName, string namespaceURI)
54 _localName = localName;
55 _namespaceURI = namespaceURI;
58 public Element(string localName, string namespaceURI): this(null, localName, namespaceURI)
70 public string LocalName
78 public string NamespaceURI
86 public MethodInfo ParseMethod {
87 get { return _parseMethod; }
88 set { _parseMethod = value; }
91 public override bool Equals(object obj)
93 Element element = obj as Element;
94 return (_localName == XmlConvert.DecodeName(element._localName) &&
95 _namespaceURI == XmlConvert.DecodeName(element._namespaceURI))?true:false;
98 public override int GetHashCode()
100 return (String.Format("{0} {1}",
101 XmlConvert.DecodeName(_localName),
102 XmlConvert.DecodeName(_namespaceURI))).GetHashCode();
105 public override string ToString()
107 return string.Format("Element.Prefix = {0}, Element.LocalName = {1}, Element.NamespaceURI = {2}", this.Prefix, this.LocalName, this.NamespaceURI);
111 internal class SoapTypeMapper
113 private static Hashtable xmlNodeToTypeTable = new Hashtable();
114 private static Hashtable typeToXmlNodeTable = new Hashtable();
115 public static readonly string SoapEncodingNamespace = "http://schemas.xmlsoap.org/soap/encoding/";
116 public static readonly string SoapEncodingPrefix = "SOAP-ENC";
117 public static readonly string SoapEnvelopeNamespace = "http://schemas.xmlsoap.org/soap/envelope/";
118 public static readonly string SoapEnvelopePrefix = "SOAP-ENV";
119 //internal static readonly string SoapEnvelope;
120 private XmlTextWriter _xmlWriter;
121 private long _prefixNumber;
122 private Hashtable namespaceToPrefixTable = new Hashtable();
123 private SerializationBinder _binder;
124 private static ArrayList _canBeValueTypeList;
125 private FormatterAssemblyStyle _assemblyFormat = FormatterAssemblyStyle.Full;
126 private Element elementString;
129 // Constructor used by SoapReader
130 public SoapTypeMapper(SerializationBinder binder)
135 // Constructor used by SoapWriter
136 public SoapTypeMapper(
137 XmlTextWriter xmlWriter,
138 FormatterAssemblyStyle assemblyFormat,
139 FormatterTypeStyle typeFormat)
141 _xmlWriter = xmlWriter;
142 _assemblyFormat = assemblyFormat;
144 //Type elementType = typeof(string);
145 if(typeFormat == FormatterTypeStyle.XsdString)
147 elementString = new Element("xsd", "string", XmlSchema.Namespace);
151 elementString = new Element(SoapEncodingPrefix, "string", SoapEncodingNamespace);
153 // typeToXmlNodeTable.Add(elementType.AssemblyQualifiedName, element);
156 static SoapTypeMapper() {
157 // SoapEnvelope = String.Format(
158 // "<{0}:Envelope xmlns:{0}='{1}' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='{2}' xmlns:{3}='{4}' xmlns:clr='{5}' SOAP-ENV:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>",
159 // SoapEnvelopePrefix,
160 // SoapEnvelopeNamespace,
161 // XmlSchema.Namespace,
162 // SoapEncodingPrefix,
163 // SoapEncodingNamespace,
164 // SoapServices.XmlNsForClrType);
165 _canBeValueTypeList = new ArrayList();
166 _canBeValueTypeList.Add(typeof(DateTime).ToString());
167 _canBeValueTypeList.Add(typeof(TimeSpan).ToString());
168 _canBeValueTypeList.Add(typeof(string).ToString());
169 _canBeValueTypeList.Add(typeof(decimal).ToString());
170 _canBeValueTypeList.Sort();
175 static string GetKey (string localName, string namespaceUri)
177 return localName + " " + namespaceUri;
180 /* public Type this [Element element]
184 public Type GetType (string xmlName, string xmlNamespace)
188 string localName = XmlConvert.DecodeName (xmlName);
189 string namespaceURI = XmlConvert.DecodeName (xmlNamespace);
190 string typeNamespace, assemblyName;
192 SoapServices.DecodeXmlNamespaceForClrTypeNamespace(
197 string typeName = typeNamespace + Type.Delimiter + localName;
199 if(assemblyName != null && assemblyName != string.Empty && _binder != null)
201 type = _binder.BindToType(assemblyName, typeName);
205 string assemblyQualifiedName = (string)xmlNodeToTypeTable [GetKey (xmlName, xmlNamespace)];
206 if(assemblyQualifiedName != null)
207 type = Type.GetType(assemblyQualifiedName);
210 type = Type.GetType(xmlName);
214 type = Type.GetType(typeName);
218 if(assemblyName == null || assemblyName == String.Empty)
219 throw new SerializationException(
220 String.Format("Parse Error, no assembly associated with XML key {0} {1}",
223 type = FormatterServices.GetTypeFromAssembly(
224 Assembly.Load(assemblyName),
230 throw new SerializationException();
235 public Element GetXmlElement (string typeFullName, string assemblyName)
238 string typeNamespace = string.Empty;
239 string typeName = typeFullName;
240 if(_assemblyFormat == FormatterAssemblyStyle.Simple)
242 string[] items = assemblyName.Split(',');
243 assemblyName = items[0];
245 string assemblyQualifiedName = typeFullName + ", " + assemblyName;
246 element = (Element) typeToXmlNodeTable[assemblyQualifiedName];
249 int typeNameIndex = typeFullName.LastIndexOf('.');
250 if(typeNameIndex != -1)
252 typeNamespace = typeFullName.Substring(0, typeNameIndex);
253 typeName = typeFullName.Substring(typeNamespace.Length + 1);
255 string namespaceURI =
256 SoapServices.CodeXmlNamespaceForClrTypeNamespace(
258 (!assemblyName.StartsWith("mscorlib"))?assemblyName:String.Empty);
259 string prefix = (string) namespaceToPrefixTable[namespaceURI];
260 if(prefix == null || prefix == string.Empty)
262 prefix = "a" + (_prefixNumber++).ToString();
263 namespaceToPrefixTable[namespaceURI] = prefix;
267 int i = typeName.IndexOf ("[");
269 typeName = XmlConvert.EncodeName (typeName.Substring (0, i)) + typeName.Substring (i);
272 int j = typeName.IndexOf ("&");
274 typeName = XmlConvert.EncodeName (typeName.Substring (0, j)) + typeName.Substring (j);
276 typeName = XmlConvert.EncodeName (typeName);
279 element = new Element(
287 public Element GetXmlElement (Type type)
289 if(type == typeof(string)) return elementString;
290 Element element = (Element) typeToXmlNodeTable[type.AssemblyQualifiedName];
293 element = GetXmlElement (type.FullName, type.Assembly.FullName);
294 // if(_assemblyFormat == FormatterAssemblyStyle.Full)
295 // element = this[type.FullName, type.Assembly.FullName];
297 // element = this[type.FullName, type.Assembly.GetName().Name];
300 else if (_xmlWriter != null)
302 element = new Element((element.Prefix == null)?_xmlWriter.LookupPrefix(element.NamespaceURI):element.Prefix, element.LocalName, element.NamespaceURI);
305 throw new SerializationException("Oooops");
309 static void RegisterType (Type type, string name, string namspace)
311 RegisterType (type, name, namspace, true);
314 static Element RegisterType (Type type, string name, string namspace, bool reverseMap)
316 Element element = new Element (name, namspace);
317 xmlNodeToTypeTable.Add (GetKey (name, namspace), type.AssemblyQualifiedName);
319 typeToXmlNodeTable.Add (type.AssemblyQualifiedName, element);
323 static void RegisterType (Type type)
325 string name = (string) type.GetProperty ("XsdType", BindingFlags.Public | BindingFlags.Static).GetValue (null, null);
326 Element element = RegisterType (type, name, XmlSchema.Namespace, true);
327 element.ParseMethod = type.GetMethod ("Parse", BindingFlags.Public | BindingFlags.Static);
328 if (element.ParseMethod == null)
329 throw new InvalidOperationException ("Parse method not found in class " + type);
332 private static void InitMappingTables()
334 RegisterType (typeof(System.Array), "Array", SoapEncodingNamespace);
335 RegisterType (typeof(string), "string", XmlSchema.Namespace, false);
336 RegisterType (typeof(string), "string", SoapEncodingNamespace, false);
337 RegisterType (typeof(bool), "boolean", XmlSchema.Namespace);
338 RegisterType (typeof(sbyte), "byte", XmlSchema.Namespace);
339 RegisterType (typeof(byte), "unsignedByte", XmlSchema.Namespace);
340 RegisterType (typeof(long), "long", XmlSchema.Namespace);
341 RegisterType (typeof(ulong), "unsignedLong", XmlSchema.Namespace);
342 RegisterType (typeof(int), "int", XmlSchema.Namespace);
343 RegisterType (typeof(uint), "unsignedInt", XmlSchema.Namespace);
344 RegisterType (typeof(float), "float", XmlSchema.Namespace);
345 RegisterType (typeof(double), "double", XmlSchema.Namespace);
346 RegisterType (typeof(decimal), "decimal", XmlSchema.Namespace);
347 RegisterType (typeof(short), "short", XmlSchema.Namespace);
348 RegisterType (typeof(ushort), "unsignedShort", XmlSchema.Namespace);
349 RegisterType (typeof(object), "anyType", XmlSchema.Namespace);
350 RegisterType (typeof(DateTime), "dateTime", XmlSchema.Namespace);
351 RegisterType (typeof(TimeSpan), "duration", XmlSchema.Namespace);
352 RegisterType (typeof(SoapFault), "Fault", SoapEnvelopeNamespace);
353 RegisterType (typeof(byte[]), "base64", SoapEncodingNamespace);
354 RegisterType (typeof(MethodSignature), "methodSignature", SoapEncodingNamespace);
355 RegisterType (typeof(SoapAnyUri));
356 RegisterType (typeof(SoapEntity));
357 RegisterType (typeof(SoapMonth));
358 RegisterType (typeof(SoapNonNegativeInteger));
359 RegisterType (typeof(SoapToken));
360 RegisterType (typeof(SoapBase64Binary));
361 RegisterType (typeof(SoapHexBinary));
362 RegisterType (typeof(SoapMonthDay));
363 RegisterType (typeof(SoapNonPositiveInteger));
364 RegisterType (typeof(SoapYear));
365 RegisterType (typeof(SoapDate));
366 RegisterType (typeof(SoapId));
367 RegisterType (typeof(SoapName));
368 RegisterType (typeof(SoapNormalizedString));
369 RegisterType (typeof(SoapYearMonth));
370 RegisterType (typeof(SoapIdref));
371 RegisterType (typeof(SoapNcName));
372 RegisterType (typeof(SoapNotation));
373 RegisterType (typeof(SoapDay));
374 RegisterType (typeof(SoapIdrefs));
375 RegisterType (typeof(SoapNegativeInteger));
376 RegisterType (typeof(SoapPositiveInteger));
377 RegisterType (typeof(SoapInteger));
378 RegisterType (typeof(SoapNmtoken));
379 RegisterType (typeof(SoapQName));
380 RegisterType (typeof(SoapEntities));
381 RegisterType (typeof(SoapLanguage));
382 RegisterType (typeof(SoapNmtokens));
383 RegisterType (typeof(SoapTime));
386 public static string GetXsdValue (object value)
388 if (value is DateTime) {
389 return SoapDateTime.ToString ((DateTime)value);
391 else if (value is decimal) {
392 return ((decimal) value).ToString (CultureInfo.InvariantCulture);
394 else if (value is double) {
395 return ((double) value).ToString ("G17", CultureInfo.InvariantCulture);
397 else if (value is float) {
398 return ((float) value).ToString ("G9", CultureInfo.InvariantCulture);
400 else if (value is TimeSpan) {
401 return SoapDuration.ToString ((TimeSpan)value);
403 else if (value is bool) {
404 return ((bool) value) ? "true" : "false";
406 else if (value is MethodSignature) {
410 return value.ToString ();
414 public static object ParseXsdValue (string value, Type type)
416 if (type == typeof(DateTime)) {
417 return SoapDateTime.Parse (value);
419 else if (type == typeof(decimal)) {
420 return decimal.Parse (value, CultureInfo.InvariantCulture);
422 else if (type == typeof(double)) {
423 return double.Parse (value, CultureInfo.InvariantCulture);
425 else if (type == typeof(float)) {
426 return float.Parse (value, CultureInfo.InvariantCulture);
428 else if (type == typeof (TimeSpan)) {
429 return SoapDuration.Parse (value);
431 else if(type.IsEnum) {
432 return Enum.Parse(type, value);
435 return Convert.ChangeType (value, type, CultureInfo.InvariantCulture);
439 public static bool CanBeValue (Type type)
441 if(type.IsPrimitive) return true;
442 if(type.IsEnum) return true;
443 if(_canBeValueTypeList.BinarySearch(type.ToString()) >= 0)
450 public bool IsInternalSoapType (Type type)
452 if (CanBeValue (type))
454 if (typeof(ISoapXsd).IsAssignableFrom (type))
456 if (type == typeof (MethodSignature))
461 public object ReadInternalSoapValue (SoapReader reader, Type type)
463 if (CanBeValue (type))
464 return ParseXsdValue (reader.XmlReader.ReadElementString (), type);
466 if (type == typeof(MethodSignature)) {
467 return MethodSignature.ReadXmlValue (reader);
470 string val = reader.XmlReader.ReadElementString ();
472 Element elem = GetXmlElement (type);
473 if (elem.ParseMethod != null)
474 return elem.ParseMethod.Invoke (null, new object[] { val });
476 throw new SerializationException ("Can't parse type " + type);
479 public string GetInternalSoapValue (SoapWriter writer, object value)
481 if (CanBeValue (value.GetType()))
482 return GetXsdValue (value);
483 else if (value is MethodSignature)
484 return ((MethodSignature)value).GetXmlValue (writer);
486 return value.ToString ();
490 class MethodSignature
494 public MethodSignature (Type[] types)
499 public static object ReadXmlValue (SoapReader reader)
501 reader.XmlReader.MoveToElement ();
502 if (reader.XmlReader.IsEmptyElement) {
503 reader.XmlReader.Skip ();
506 reader.XmlReader.ReadStartElement ();
507 string names = reader.XmlReader.ReadString ();
508 while (reader.XmlReader.NodeType != XmlNodeType.EndElement)
509 reader.XmlReader.Skip ();
511 ArrayList types = new ArrayList ();
512 string[] tns = names.Split (' ');
513 foreach (string tn in tns) {
514 if (tn.Length == 0) continue;
515 types.Add (reader.GetTypeFromQName (tn));
517 reader.XmlReader.ReadEndElement ();
518 return (Type[]) types.ToArray (typeof(Type));
521 public string GetXmlValue (SoapWriter writer)
523 StringBuilder sb = new StringBuilder ();
524 foreach (Type t in types) {
525 Element elem = writer.Mapper.GetXmlElement (t);
526 if (sb.Length > 0) sb.Append (' ');
527 string prefix = writer.GetNamespacePrefix (elem);
528 sb.Append (prefix).Append (':').Append (elem.LocalName);
530 return sb.ToString ();