2 // XmlSerializationWriterInterpreter.cs:
5 // Lluis Sanchez Gual (lluis@ximian.com)
7 // (C) 2002, 2003 Ximian, Inc. http://www.ximian.com
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System
.Collections
;
34 using System
.Reflection
;
35 using System
.Xml
.Schema
;
37 namespace System
.Xml
.Serialization
39 internal class XmlSerializationWriterInterpreter
: XmlSerializationWriter
42 SerializationFormat _format
;
43 const string xmlNamespace
= "http://www.w3.org/2000/xmlns/";
45 public XmlSerializationWriterInterpreter (XmlMapping typeMap
)
48 _format
= typeMap
.Format
;
51 protected override void InitCallbacks ()
53 ArrayList maps
= _typeMap
.RelatedMaps
;
56 foreach (XmlTypeMapping map
in maps
) {
57 CallbackInfo info
= new CallbackInfo (this, map
);
58 if (map
.TypeData
.SchemaType
== SchemaTypes
.Enum
) AddWriteCallback(map
.TypeData
.Type
, map
.XmlType
, map
.Namespace
, new XmlSerializationWriteCallback (info
.WriteEnum
));
59 else AddWriteCallback(map
.TypeData
.Type
, map
.XmlType
, map
.Namespace
, new XmlSerializationWriteCallback (info
.WriteObject
));
64 public void WriteRoot (object ob
)
66 WriteStartDocument ();
68 if (_typeMap
is XmlTypeMapping
)
70 XmlTypeMapping mp
= (XmlTypeMapping
) _typeMap
;
71 if (mp
.TypeData
.SchemaType
== SchemaTypes
.Class
|| mp
.TypeData
.SchemaType
== SchemaTypes
.Array
)
74 if (_format
== SerializationFormat
.Literal
)
75 WriteObject (mp
, ob
, mp
.ElementName
, mp
.Namespace
, true, false, true);
77 WritePotentiallyReferencingElement (mp
.ElementName
, mp
.Namespace
, ob
, mp
.TypeData
.Type
, true, false);
79 else if (ob
is object[])
80 WriteMessage ((XmlMembersMapping
)_typeMap
, (object[]) ob
);
82 throw CreateUnknownTypeException (ob
);
84 WriteReferencedElements ();
87 protected XmlTypeMapping
GetTypeMap (Type type
)
89 ArrayList maps
= _typeMap
.RelatedMaps
;
92 foreach (XmlTypeMapping map
in maps
)
93 if (map
.TypeData
.Type
== type
) return map
;
95 throw new InvalidOperationException ("Type " + type
+ " not mapped");
98 protected virtual void WriteObject (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
, bool isNullable
, bool needType
, bool writeWrappingElem
)
104 if (_format
== SerializationFormat
.Literal
) WriteNullTagLiteral(element
, namesp
);
105 else WriteNullTagEncoded (element
, namesp
);
112 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral((XmlNode
)ob
, "", "", true, false);
113 else WriteElementEncoded((XmlNode
)ob
, "", "", true, false);
117 if (typeMap
.TypeData
.SchemaType
== SchemaTypes
.XmlSerializable
)
119 WriteSerializable ((IXmlSerializable
)ob
, element
, namesp
, isNullable
);
123 XmlTypeMapping map
= typeMap
.GetRealTypeMap (ob
.GetType());
128 if (ob
.GetType ().IsArray
&& typeof (XmlNode
).IsAssignableFrom (ob
.GetType ().GetElementType ())) {
129 Writer
.WriteStartElement (element
, namesp
);
130 foreach (XmlNode node
in (IEnumerable
) ob
)
131 node
.WriteTo (Writer
);
132 Writer
.WriteEndElement ();
135 WriteTypedPrimitive (element
, namesp
, ob
, true);
139 if (writeWrappingElem
)
141 if (map
!= typeMap
|| _format
== SerializationFormat
.Encoded
) needType
= true;
142 WriteStartElement (element
, namesp
, ob
);
146 WriteXsiType(map
.XmlType
, map
.XmlTypeNamespace
);
148 switch (map
.TypeData
.SchemaType
)
150 case SchemaTypes
.Class
: WriteObjectElement (map
, ob
, element
, namesp
); break;
151 case SchemaTypes
.Array
: WriteListElement (map
, ob
, element
, namesp
); break;
152 case SchemaTypes
.Primitive
: WritePrimitiveElement (map
, ob
, element
, namesp
); break;
153 case SchemaTypes
.Enum
: WriteEnumElement (map
, ob
, element
, namesp
); break;
156 if (writeWrappingElem
)
157 WriteEndElement (ob
);
160 protected virtual void WriteMessage (XmlMembersMapping membersMap
, object[] parameters
)
162 if (membersMap
.HasWrapperElement
) {
164 WriteStartElement(membersMap
.ElementName
, membersMap
.Namespace
, (_format
== SerializationFormat
.Encoded
));
166 if (Writer
.LookupPrefix (XmlSchema
.Namespace
) == null)
167 WriteAttribute ("xmlns","xsd",XmlSchema
.Namespace
,XmlSchema
.Namespace
);
169 if (Writer
.LookupPrefix (XmlSchema
.InstanceNamespace
) == null)
170 WriteAttribute ("xmlns","xsi",XmlSchema
.InstanceNamespace
,XmlSchema
.InstanceNamespace
);
173 WriteMembers ((ClassMap
)membersMap
.ObjectMap
, parameters
, true);
175 if (membersMap
.HasWrapperElement
)
179 protected virtual void WriteObjectElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
181 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
182 if (map
.NamespaceDeclarations
!= null)
183 WriteNamespaceDeclarations ((XmlSerializerNamespaces
) map
.NamespaceDeclarations
.GetValue (ob
));
185 WriteObjectElementAttributes (typeMap
, ob
);
186 WriteObjectElementElements (typeMap
, ob
);
189 protected virtual void WriteObjectElementAttributes (XmlTypeMapping typeMap
, object ob
)
191 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
192 WriteAttributeMembers (map
, ob
, false);
195 protected virtual void WriteObjectElementElements (XmlTypeMapping typeMap
, object ob
)
197 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
198 WriteElementMembers (map
, ob
, false);
201 void WriteMembers (ClassMap map
, object ob
, bool isValueList
)
203 WriteAttributeMembers (map
, ob
, isValueList
);
204 WriteElementMembers (map
, ob
, isValueList
);
207 void WriteAttributeMembers (ClassMap map
, object ob
, bool isValueList
)
211 XmlTypeMapMember anyAttrMember
= map
.DefaultAnyAttributeMember
;
212 if (anyAttrMember
!= null && MemberHasValue (anyAttrMember
, ob
, isValueList
))
214 ICollection extraAtts
= (ICollection
) GetMemberValue (anyAttrMember
, ob
, isValueList
);
215 if (extraAtts
!= null)
217 foreach (XmlAttribute attr
in extraAtts
)
218 if (attr
.NamespaceURI
!= xmlNamespace
)
219 WriteXmlAttribute (attr
, ob
);
223 ICollection attributes
= map
.AttributeMembers
;
224 if (attributes
!= null)
226 foreach (XmlTypeMapMemberAttribute attr
in attributes
) {
227 if (MemberHasValue (attr
, ob
, isValueList
))
228 WriteAttribute (attr
.AttributeName
, attr
.Namespace
, GetStringValue (attr
.MappedType
, attr
.TypeData
, GetMemberValue (attr
, ob
, isValueList
)));
233 void WriteElementMembers (ClassMap map
, object ob
, bool isValueList
)
235 ICollection members
= map
.ElementMembers
;
238 foreach (XmlTypeMapMemberElement member
in members
)
240 if (!MemberHasValue (member
, ob
, isValueList
)) continue;
241 object memberValue
= GetMemberValue (member
, ob
, isValueList
);
242 Type memType
= member
.GetType();
244 if (memType
== typeof(XmlTypeMapMemberList
))
246 WriteMemberElement ((XmlTypeMapElementInfo
) member
.ElementInfo
[0], memberValue
);
248 else if (memType
== typeof(XmlTypeMapMemberFlatList
))
250 if (memberValue
!= null)
251 WriteListContent (ob
, member
.TypeData
, ((XmlTypeMapMemberFlatList
)member
).ListMap
, memberValue
, null);
253 else if (memType
== typeof(XmlTypeMapMemberAnyElement
))
255 if (memberValue
!= null)
256 WriteAnyElementContent ((XmlTypeMapMemberAnyElement
)member
, memberValue
);
258 else if (memType
== typeof(XmlTypeMapMemberAnyAttribute
))
262 else if (memType
== typeof(XmlTypeMapMemberElement
))
264 XmlTypeMapElementInfo elem
= member
.FindElement (ob
, memberValue
);
265 WriteMemberElement (elem
, memberValue
);
268 throw new InvalidOperationException ("Unknown member type");
273 object GetMemberValue (XmlTypeMapMember member
, object ob
, bool isValueList
)
275 if (isValueList
) return ((object[])ob
)[member
.GlobalIndex
];
276 else return member
.GetValue (ob
);
279 bool MemberHasValue (XmlTypeMapMember member
, object ob
, bool isValueList
)
282 return member
.GlobalIndex
< ((object[])ob
).Length
;
284 else if (member
.DefaultValue
!= System
.DBNull
.Value
) {
285 object val
= GetMemberValue (member
, ob
, isValueList
);
286 if (val
== null && member
.DefaultValue
== null) return false;
287 if (val
!= null && val
.GetType().IsEnum
)
289 if (val
.Equals (member
.DefaultValue
)) return false;
290 Type t
= Enum
.GetUnderlyingType(val
.GetType());
291 val
= Convert
.ChangeType (val
, t
);
293 if (val
!= null && val
.Equals (member
.DefaultValue
)) return false;
295 else if (member
.IsOptionalValueType
)
296 return member
.GetValueSpecified (ob
);
301 void WriteMemberElement (XmlTypeMapElementInfo elem
, object memberValue
)
303 switch (elem
.TypeData
.SchemaType
)
305 case SchemaTypes
.XmlNode
:
306 string elemName
= elem
.WrappedElement
? elem
.ElementName
: "";
307 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral(((XmlNode
)memberValue
), elemName
, elem
.Namespace
, elem
.IsNullable
, false);
308 else WriteElementEncoded(((XmlNode
)memberValue
), elemName
, elem
.Namespace
, elem
.IsNullable
, false);
311 case SchemaTypes
.Enum
:
312 case SchemaTypes
.Primitive
:
313 if (_format
== SerializationFormat
.Literal
)
314 WritePrimitiveValueLiteral (memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.MappedType
, elem
.TypeData
, elem
.WrappedElement
, elem
.IsNullable
);
316 WritePrimitiveValueEncoded (memberValue
, elem
.ElementName
, elem
.Namespace
, new XmlQualifiedName (elem
.DataTypeName
, elem
.DataTypeNamespace
), elem
.MappedType
, elem
.TypeData
, elem
.WrappedElement
, elem
.IsNullable
);
319 case SchemaTypes
.Array
:
320 if (memberValue
== null) {
321 if (!elem
.IsNullable
) return;
322 if (_format
== SerializationFormat
.Literal
) WriteNullTagLiteral (elem
.ElementName
, elem
.Namespace
);
323 else WriteNullTagEncoded (elem
.ElementName
, elem
.Namespace
);
325 else if (elem
.MappedType
.MultiReferenceType
)
326 WriteReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, elem
.IsNullable
);
328 WriteStartElement(elem
.ElementName
, elem
.Namespace
, memberValue
);
329 WriteListContent (null, elem
.TypeData
, (ListMap
) elem
.MappedType
.ObjectMap
, memberValue
, null);
330 WriteEndElement (memberValue
);
334 case SchemaTypes
.Class
:
335 if (elem
.MappedType
.MultiReferenceType
) {
336 if (elem
.MappedType
.TypeData
.Type
== typeof(object))
337 WritePotentiallyReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, null, false, elem
.IsNullable
);
339 WriteReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, elem
.IsNullable
);
341 else WriteObject (elem
.MappedType
, memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.IsNullable
, false, true);
344 case SchemaTypes
.XmlSerializable
:
346 if (!elem
.MappedType
.TypeData
.Type
.IsInstanceOfType (memberValue
))
347 memberValue
= ImplicitConvert (memberValue
, elem
.MappedType
.TypeData
.Type
);
348 WriteSerializable ((IXmlSerializable
) memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.IsNullable
);
352 throw new NotSupportedException ("Invalid value type");
356 object ImplicitConvert (object obj
, Type type
)
360 for (Type t
= type
; t
!= typeof (object); t
= t
.BaseType
) {
361 MethodInfo mi
= t
.GetMethod ("op_Implicit", new Type
[] {t}
);
362 if (mi
!= null && mi
.ReturnType
.IsAssignableFrom (obj
.GetType ()))
363 return mi
.Invoke (null, new object [] {obj}
);
366 for (Type t
= obj
.GetType (); t
!= typeof (object); t
= t
.BaseType
) {
367 MethodInfo mi
= t
.GetMethod ("op_Implicit", new Type
[] {t}
);
368 if (mi
!= null && mi
.ReturnType
== type
)
369 return mi
.Invoke (null, new object [] {obj}
);
374 void WritePrimitiveValueLiteral (object memberValue
, string name
, string ns
, XmlTypeMapping mappedType
, TypeData typeData
, bool wrapped
, bool isNullable
)
377 WriteValue (GetStringValue (mappedType
, typeData
, memberValue
));
379 else if (isNullable
) {
380 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteNullableQualifiedNameLiteral (name
, ns
, (XmlQualifiedName
)memberValue
);
381 else WriteNullableStringLiteral (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
));
384 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteElementQualifiedName (name
, ns
, (XmlQualifiedName
)memberValue
);
385 else WriteElementString (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
));
389 void WritePrimitiveValueEncoded (object memberValue
, string name
, string ns
, XmlQualifiedName xsiType
, XmlTypeMapping mappedType
, TypeData typeData
, bool wrapped
, bool isNullable
)
392 WriteValue (GetStringValue (mappedType
, typeData
, memberValue
));
394 else if (isNullable
) {
395 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteNullableQualifiedNameEncoded (name
, ns
, (XmlQualifiedName
)memberValue
, xsiType
);
396 else WriteNullableStringEncoded (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
), xsiType
);
399 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteElementQualifiedName (name
, ns
, (XmlQualifiedName
)memberValue
, xsiType
);
400 else WriteElementString (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
), xsiType
);
404 protected virtual void WriteListElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
406 if (_format
== SerializationFormat
.Encoded
)
409 int itemCount
= GetListCount (typeMap
.TypeData
, ob
);
410 ((ListMap
) typeMap
.ObjectMap
).GetArrayType (itemCount
, out n
, out ns
);
411 string arrayType
= (ns
!= string.Empty
) ? FromXmlQualifiedName (new XmlQualifiedName(n
,ns
)) : n
;
412 WriteAttribute ("arrayType", XmlSerializer
.EncodingNamespace
, arrayType
);
414 WriteListContent (null, typeMap
.TypeData
, (ListMap
) typeMap
.ObjectMap
, ob
, null);
417 void WriteListContent (object container
, TypeData listType
, ListMap map
, object ob
, StringBuilder targetString
)
419 if (listType
.Type
.IsArray
)
421 Array array
= (Array
)ob
;
422 for (int n
=0; n
<array
.Length
; n
++)
424 object item
= array
.GetValue (n
);
425 XmlTypeMapElementInfo info
= map
.FindElement (container
, n
, item
);
426 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
427 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
428 else if (item
!= null) throw CreateUnknownTypeException (item
);
431 else if (ob
is ICollection
)
433 int count
= (int) ob
.GetType().GetProperty ("Count").GetValue(ob
,null);
434 PropertyInfo itemProp
= TypeData
.GetIndexerProperty (listType
.Type
);
435 object[] index
= new object[1];
436 for (int n
=0; n
<count
; n
++)
439 object item
= itemProp
.GetValue (ob
, index
);
440 XmlTypeMapElementInfo info
= map
.FindElement (container
, n
, item
);
441 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
442 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
443 else if (item
!= null) throw CreateUnknownTypeException (item
);
446 else if (ob
is IEnumerable
)
448 IEnumerable e
= (IEnumerable
)ob
;
449 foreach (object item
in e
)
451 XmlTypeMapElementInfo info
= map
.FindElement (container
, -1, item
);
452 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
453 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
454 else if (item
!= null) throw CreateUnknownTypeException (item
);
458 throw new Exception ("Unsupported collection type");
461 int GetListCount (TypeData listType
, object ob
)
463 if (listType
.Type
.IsArray
)
464 return ((Array
)ob
).Length
;
466 return (int) listType
.Type
.GetProperty ("Count").GetValue(ob
,null);
469 void WriteAnyElementContent (XmlTypeMapMemberAnyElement member
, object memberValue
)
471 if (member
.TypeData
.Type
== typeof (XmlElement
)) {
472 memberValue
= new object[] { memberValue }
;
475 Array elems
= (Array
) memberValue
;
476 foreach (var elem_
in elems
)
478 XmlNode elem
= elem_
as XmlNode
;
480 throw new InvalidOperationException (String
.Format ("XmlAnyElementAttribute can only be applied to members of type XmlElement, XmlElement[] or XmlNode[]. The target object is {0}", elem_
!= null ? elem_
.GetType () : null));
481 if (elem
is XmlElement
)
483 if (member
.IsElementDefined (elem
.Name
, elem
.NamespaceURI
))
485 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral (elem
, "", "", false, true);
486 else WriteElementEncoded (elem
, "", "", false, true);
489 throw CreateUnknownAnyElementException (elem
.Name
, elem
.NamespaceURI
);
492 elem
.WriteTo (Writer
);
496 protected virtual void WritePrimitiveElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
498 Writer
.WriteString (GetStringValue (typeMap
, typeMap
.TypeData
, ob
));
501 protected virtual void WriteEnumElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
503 Writer
.WriteString (GetEnumXmlValue (typeMap
, ob
));
506 string GetStringValue (XmlTypeMapping typeMap
, TypeData type
, object value)
508 if (type
.SchemaType
== SchemaTypes
.Array
) {
509 if (value == null) return null;
510 StringBuilder sb
= new StringBuilder ();
511 WriteListContent (null, typeMap
.TypeData
, (ListMap
)typeMap
.ObjectMap
, value, sb
);
512 return sb
.ToString ().Trim ();
514 else if (type
.SchemaType
== SchemaTypes
.Enum
)
515 return GetEnumXmlValue (typeMap
, value);
516 else if (type
.Type
== typeof (XmlQualifiedName
))
517 return FromXmlQualifiedName ((XmlQualifiedName
)value);
518 else if (value == null)
521 return XmlCustomFormatter
.ToXmlString (type
, value);
524 string GetEnumXmlValue (XmlTypeMapping typeMap
, object ob
)
528 EnumMap map
= (EnumMap
)typeMap
.ObjectMap
;
529 return map
.GetXmlName (typeMap
.TypeFullName
, ob
);
534 XmlSerializationWriterInterpreter _swi
;
535 XmlTypeMapping _typeMap
;
537 public CallbackInfo (XmlSerializationWriterInterpreter swi
, XmlTypeMapping typeMap
)
543 internal void WriteObject (object ob
)
545 _swi
.WriteObject (_typeMap
, ob
, _typeMap
.ElementName
, _typeMap
.Namespace
, false, false, false);
548 internal void WriteEnum (object ob
)
550 _swi
.WriteObject (_typeMap
, ob
, _typeMap
.ElementName
, _typeMap
.Namespace
, false, true, false);