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
;
44 public XmlSerializationWriterInterpreter (XmlMapping typeMap
)
47 _format
= typeMap
.Format
;
50 protected override void InitCallbacks ()
52 ArrayList maps
= _typeMap
.RelatedMaps
;
55 foreach (XmlTypeMapping map
in maps
) {
56 CallbackInfo info
= new CallbackInfo (this, map
);
57 if (map
.TypeData
.SchemaType
== SchemaTypes
.Enum
) AddWriteCallback(map
.TypeData
.Type
, map
.XmlType
, map
.Namespace
, new XmlSerializationWriteCallback (info
.WriteEnum
));
58 else AddWriteCallback(map
.TypeData
.Type
, map
.XmlType
, map
.Namespace
, new XmlSerializationWriteCallback (info
.WriteObject
));
63 public void WriteRoot (object ob
)
65 WriteStartDocument ();
67 if (_typeMap
is XmlTypeMapping
)
69 XmlTypeMapping mp
= (XmlTypeMapping
) _typeMap
;
70 if (mp
.TypeData
.SchemaType
== SchemaTypes
.Class
|| mp
.TypeData
.SchemaType
== SchemaTypes
.Array
)
73 if (_format
== SerializationFormat
.Literal
)
74 WriteObject (mp
, ob
, mp
.ElementName
, mp
.Namespace
, true, false, true);
76 WritePotentiallyReferencingElement (mp
.ElementName
, mp
.Namespace
, ob
, mp
.TypeData
.Type
, true, false);
78 else if (ob
is object[])
79 WriteMessage ((XmlMembersMapping
)_typeMap
, (object[]) ob
);
81 throw CreateUnknownTypeException (ob
);
83 WriteReferencedElements ();
86 protected XmlTypeMapping
GetTypeMap (Type type
)
88 ArrayList maps
= _typeMap
.RelatedMaps
;
91 foreach (XmlTypeMapping map
in maps
)
92 if (map
.TypeData
.Type
== type
) return map
;
94 throw new InvalidOperationException ("Type " + type
+ " not mapped");
97 protected virtual void WriteObject (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
, bool isNullable
, bool needType
, bool writeWrappingElem
)
103 if (_format
== SerializationFormat
.Literal
) WriteNullTagLiteral(element
, namesp
);
104 else WriteNullTagEncoded (element
, namesp
);
111 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral((XmlNode
)ob
, "", "", true, false);
112 else WriteElementEncoded((XmlNode
)ob
, "", "", true, false);
116 if (typeMap
.TypeData
.SchemaType
== SchemaTypes
.XmlSerializable
)
118 WriteSerializable ((IXmlSerializable
)ob
, element
, namesp
, isNullable
);
122 XmlTypeMapping map
= typeMap
.GetRealTypeMap (ob
.GetType().FullName
);
126 WriteTypedPrimitive (element
, namesp
, ob
, true);
130 if (writeWrappingElem
)
132 if (map
!= typeMap
|| _format
== SerializationFormat
.Encoded
) needType
= true;
133 WriteStartElement (element
, namesp
, ob
);
137 WriteXsiType(map
.XmlType
, map
.XmlTypeNamespace
);
139 switch (map
.TypeData
.SchemaType
)
141 case SchemaTypes
.Class
: WriteObjectElement (map
, ob
, element
, namesp
); break;
142 case SchemaTypes
.Array
: WriteListElement (map
, ob
, element
, namesp
); break;
143 case SchemaTypes
.Primitive
: WritePrimitiveElement (map
, ob
, element
, namesp
); break;
144 case SchemaTypes
.Enum
: WriteEnumElement (map
, ob
, element
, namesp
); break;
147 if (writeWrappingElem
)
148 WriteEndElement (ob
);
151 protected virtual void WriteMessage (XmlMembersMapping membersMap
, object[] parameters
)
153 if (membersMap
.HasWrapperElement
) {
155 WriteStartElement(membersMap
.ElementName
, membersMap
.Namespace
, (_format
== SerializationFormat
.Encoded
));
157 if (Writer
.LookupPrefix (XmlSchema
.Namespace
) == null)
158 WriteAttribute ("xmlns","xsd",XmlSchema
.Namespace
,XmlSchema
.Namespace
);
160 if (Writer
.LookupPrefix (XmlSchema
.InstanceNamespace
) == null)
161 WriteAttribute ("xmlns","xsi",XmlSchema
.InstanceNamespace
,XmlSchema
.InstanceNamespace
);
164 WriteMembers ((ClassMap
)membersMap
.ObjectMap
, parameters
, true);
166 if (membersMap
.HasWrapperElement
)
170 protected virtual void WriteObjectElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
172 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
173 if (map
.NamespaceDeclarations
!= null)
174 WriteNamespaceDeclarations ((XmlSerializerNamespaces
) map
.NamespaceDeclarations
.GetValue (ob
));
176 WriteObjectElementAttributes (typeMap
, ob
);
177 WriteObjectElementElements (typeMap
, ob
);
180 protected virtual void WriteObjectElementAttributes (XmlTypeMapping typeMap
, object ob
)
182 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
183 WriteAttributeMembers (map
, ob
, false);
186 protected virtual void WriteObjectElementElements (XmlTypeMapping typeMap
, object ob
)
188 ClassMap map
= (ClassMap
)typeMap
.ObjectMap
;
189 WriteElementMembers (map
, ob
, false);
192 void WriteMembers (ClassMap map
, object ob
, bool isValueList
)
194 WriteAttributeMembers (map
, ob
, isValueList
);
195 WriteElementMembers (map
, ob
, isValueList
);
198 void WriteAttributeMembers (ClassMap map
, object ob
, bool isValueList
)
202 ICollection attributes
= map
.AttributeMembers
;
203 if (attributes
!= null)
205 foreach (XmlTypeMapMemberAttribute attr
in attributes
) {
206 if (MemberHasValue (attr
, ob
, isValueList
))
207 WriteAttribute (attr
.AttributeName
, attr
.Namespace
, GetStringValue (attr
.MappedType
, attr
.TypeData
, GetMemberValue (attr
, ob
, 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 WriteXmlAttribute (attr
, ob
);
223 void WriteElementMembers (ClassMap map
, object ob
, bool isValueList
)
225 ICollection members
= map
.ElementMembers
;
228 foreach (XmlTypeMapMemberElement member
in members
)
230 if (!MemberHasValue (member
, ob
, isValueList
)) continue;
231 object memberValue
= GetMemberValue (member
, ob
, isValueList
);
232 Type memType
= member
.GetType();
234 if (memType
== typeof(XmlTypeMapMemberList
))
236 WriteMemberElement ((XmlTypeMapElementInfo
) member
.ElementInfo
[0], memberValue
);
238 else if (memType
== typeof(XmlTypeMapMemberFlatList
))
240 if (memberValue
!= null)
241 WriteListContent (member
.TypeData
, ((XmlTypeMapMemberFlatList
)member
).ListMap
, memberValue
, null);
243 else if (memType
== typeof(XmlTypeMapMemberAnyElement
))
245 if (memberValue
!= null)
246 WriteAnyElementContent ((XmlTypeMapMemberAnyElement
)member
, memberValue
);
248 else if (memType
== typeof(XmlTypeMapMemberAnyAttribute
))
252 else if (memType
== typeof(XmlTypeMapMemberElement
))
254 XmlTypeMapElementInfo elem
= member
.FindElement (ob
, memberValue
);
255 WriteMemberElement (elem
, memberValue
);
258 throw new InvalidOperationException ("Unknown member type");
263 object GetMemberValue (XmlTypeMapMember member
, object ob
, bool isValueList
)
265 if (isValueList
) return ((object[])ob
)[member
.Index
];
266 else return member
.GetValue (ob
);
269 bool MemberHasValue (XmlTypeMapMember member
, object ob
, bool isValueList
)
272 return member
.Index
< ((object[])ob
).Length
;
274 else if (member
.DefaultValue
!= System
.DBNull
.Value
) {
275 object val
= GetMemberValue (member
, ob
, isValueList
);
276 if (val
== null && member
.DefaultValue
== null) return false;
277 if (val
!= null && val
.GetType().IsEnum
)
279 if (val
.Equals (member
.DefaultValue
)) return false;
280 Type t
= Enum
.GetUnderlyingType(val
.GetType());
281 val
= Convert
.ChangeType (val
, t
);
283 if (val
!= null && val
.Equals (member
.DefaultValue
)) return false;
285 else if (member
.IsOptionalValueType
)
286 return member
.GetValueSpecified (ob
);
291 void WriteMemberElement (XmlTypeMapElementInfo elem
, object memberValue
)
293 switch (elem
.TypeData
.SchemaType
)
295 case SchemaTypes
.XmlNode
:
296 string elemName
= elem
.WrappedElement
? elem
.ElementName
: "";
297 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral(((XmlNode
)memberValue
), elemName
, elem
.Namespace
, elem
.IsNullable
, false);
298 else WriteElementEncoded(((XmlNode
)memberValue
), elemName
, elem
.Namespace
, elem
.IsNullable
, false);
301 case SchemaTypes
.Enum
:
302 case SchemaTypes
.Primitive
:
303 if (_format
== SerializationFormat
.Literal
)
304 WritePrimitiveValueLiteral (memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.MappedType
, elem
.TypeData
, elem
.WrappedElement
, elem
.IsNullable
);
306 WritePrimitiveValueEncoded (memberValue
, elem
.ElementName
, elem
.Namespace
, new XmlQualifiedName (elem
.TypeData
.XmlType
, elem
.DataTypeNamespace
), elem
.MappedType
, elem
.TypeData
, elem
.WrappedElement
, elem
.IsNullable
);
309 case SchemaTypes
.Array
:
310 if (memberValue
== null) {
311 if (!elem
.IsNullable
) return;
312 if (_format
== SerializationFormat
.Literal
) WriteNullTagLiteral (elem
.ElementName
, elem
.Namespace
);
313 else WriteNullTagEncoded (elem
.ElementName
, elem
.Namespace
);
315 else if (elem
.MappedType
.MultiReferenceType
)
316 WriteReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, elem
.IsNullable
);
318 WriteStartElement(elem
.ElementName
, elem
.Namespace
, memberValue
);
319 WriteListContent (elem
.TypeData
, (ListMap
) elem
.MappedType
.ObjectMap
, memberValue
, null);
320 WriteEndElement (memberValue
);
324 case SchemaTypes
.Class
:
325 if (elem
.MappedType
.MultiReferenceType
) {
326 if (elem
.MappedType
.TypeData
.Type
== typeof(object))
327 WritePotentiallyReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, null, false, elem
.IsNullable
);
329 WriteReferencingElement (elem
.ElementName
, elem
.Namespace
, memberValue
, elem
.IsNullable
);
331 else WriteObject (elem
.MappedType
, memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.IsNullable
, false, true);
334 case SchemaTypes
.XmlSerializable
:
335 WriteSerializable ((IXmlSerializable
) memberValue
, elem
.ElementName
, elem
.Namespace
, elem
.IsNullable
);
339 throw new NotSupportedException ("Invalid value type");
343 void WritePrimitiveValueLiteral (object memberValue
, string name
, string ns
, XmlTypeMapping mappedType
, TypeData typeData
, bool wrapped
, bool isNullable
)
346 WriteValue (GetStringValue (mappedType
, typeData
, memberValue
));
348 else if (isNullable
) {
349 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteNullableQualifiedNameLiteral (name
, ns
, (XmlQualifiedName
)memberValue
);
350 else WriteNullableStringLiteral (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
));
353 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteElementQualifiedName (name
, ns
, (XmlQualifiedName
)memberValue
);
354 else WriteElementString (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
));
358 void WritePrimitiveValueEncoded (object memberValue
, string name
, string ns
, XmlQualifiedName xsiType
, XmlTypeMapping mappedType
, TypeData typeData
, bool wrapped
, bool isNullable
)
361 WriteValue (GetStringValue (mappedType
, typeData
, memberValue
));
363 else if (isNullable
) {
364 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteNullableQualifiedNameEncoded (name
, ns
, (XmlQualifiedName
)memberValue
, xsiType
);
365 else WriteNullableStringEncoded (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
), xsiType
);
368 if (typeData
.Type
== typeof(XmlQualifiedName
)) WriteElementQualifiedName (name
, ns
, (XmlQualifiedName
)memberValue
, xsiType
);
369 else WriteElementString (name
, ns
, GetStringValue (mappedType
, typeData
, memberValue
), xsiType
);
373 protected virtual void WriteListElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
375 if (_format
== SerializationFormat
.Encoded
)
378 int itemCount
= GetListCount (typeMap
.TypeData
, ob
);
379 ((ListMap
) typeMap
.ObjectMap
).GetArrayType (itemCount
, out n
, out ns
);
380 string arrayType
= (ns
!= string.Empty
) ? FromXmlQualifiedName (new XmlQualifiedName(n
,ns
)) : n
;
381 WriteAttribute ("arrayType", XmlSerializer
.EncodingNamespace
, arrayType
);
383 WriteListContent (typeMap
.TypeData
, (ListMap
) typeMap
.ObjectMap
, ob
, null);
386 void WriteListContent (TypeData listType
, ListMap map
, object ob
, StringBuilder targetString
)
388 if (listType
.Type
.IsArray
)
390 Array array
= (Array
)ob
;
391 for (int n
=0; n
<array
.Length
; n
++)
393 object item
= array
.GetValue (n
);
394 XmlTypeMapElementInfo info
= map
.FindElement (item
);
395 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
396 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
397 else if (item
!= null) throw CreateUnknownTypeException (item
);
400 else if (ob
is ICollection
)
402 int count
= (int) listType
.Type
.GetProperty ("Count").GetValue(ob
,null);
403 PropertyInfo itemProp
= TypeData
.GetIndexerProperty (listType
.Type
);
404 object[] index
= new object[1];
405 for (int n
=0; n
<count
; n
++)
408 object item
= itemProp
.GetValue (ob
, index
);
409 XmlTypeMapElementInfo info
= map
.FindElement (item
);
410 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
411 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
412 else if (item
!= null) throw CreateUnknownTypeException (item
);
415 else if (ob
is IEnumerable
)
417 IEnumerable e
= (IEnumerable
)ob
;
418 foreach (object item
in e
)
420 XmlTypeMapElementInfo info
= map
.FindElement (item
);
421 if (info
!= null && targetString
== null) WriteMemberElement (info
, item
);
422 else if (info
!= null && targetString
!= null) targetString
.Append (GetStringValue (info
.MappedType
, info
.TypeData
, item
)).Append (" ");
423 else if (item
!= null) throw CreateUnknownTypeException (item
);
427 throw new Exception ("Unsupported collection type");
430 int GetListCount (TypeData listType
, object ob
)
432 if (listType
.Type
.IsArray
)
433 return ((Array
)ob
).Length
;
435 return (int) listType
.Type
.GetProperty ("Count").GetValue(ob
,null);
438 void WriteAnyElementContent (XmlTypeMapMemberAnyElement member
, object memberValue
)
440 if (member
.TypeData
.Type
== typeof (XmlElement
)) {
441 memberValue
= new object[] { memberValue }
;
444 bool canBeText
= member
.CanBeText
;
445 Array elems
= (Array
) memberValue
;
446 foreach (XmlNode elem
in elems
)
448 if (elem
is XmlElement
)
450 if (member
.IsElementDefined (elem
.Name
, elem
.NamespaceURI
))
452 if (_format
== SerializationFormat
.Literal
) WriteElementLiteral (elem
, "", "", false, true);
453 else WriteElementEncoded (elem
, "", "", false, true);
456 throw CreateUnknownAnyElementException (elem
.Name
, elem
.NamespaceURI
);
458 else if (elem
is XmlCharacterData
)
459 elem
.WriteTo (Writer
);
461 throw CreateUnknownTypeException (elem
);
465 protected virtual void WritePrimitiveElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
467 Writer
.WriteString (GetStringValue (typeMap
, typeMap
.TypeData
, ob
));
470 protected virtual void WriteEnumElement (XmlTypeMapping typeMap
, object ob
, string element
, string namesp
)
472 Writer
.WriteString (GetEnumXmlValue (typeMap
, ob
));
475 string GetStringValue (XmlTypeMapping typeMap
, TypeData type
, object value)
477 if (type
.SchemaType
== SchemaTypes
.Array
) {
478 if (value == null) return null;
479 StringBuilder sb
= new StringBuilder ();
480 WriteListContent (typeMap
.TypeData
, (ListMap
)typeMap
.ObjectMap
, value, sb
);
481 return sb
.ToString ().Trim ();
483 else if (type
.SchemaType
== SchemaTypes
.Enum
)
484 return GetEnumXmlValue (typeMap
, value);
485 else if (type
.Type
== typeof (XmlQualifiedName
))
486 return FromXmlQualifiedName ((XmlQualifiedName
)value);
487 else if (value == null)
490 return XmlCustomFormatter
.ToXmlString (type
, value);
493 string GetEnumXmlValue (XmlTypeMapping typeMap
, object ob
)
495 EnumMap map
= (EnumMap
)typeMap
.ObjectMap
;
496 return map
.GetXmlName (ob
);
501 XmlSerializationWriterInterpreter _swi
;
502 XmlTypeMapping _typeMap
;
504 public CallbackInfo (XmlSerializationWriterInterpreter swi
, XmlTypeMapping typeMap
)
510 internal void WriteObject (object ob
)
512 _swi
.WriteObject (_typeMap
, ob
, _typeMap
.ElementName
, _typeMap
.Namespace
, false, false, false);
515 internal void WriteEnum (object ob
)
517 _swi
.WriteObject (_typeMap
, ob
, _typeMap
.ElementName
, _typeMap
.Namespace
, false, true, false);