ProjectArcade
636 строк · 24.2 Кб
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Globalization;
6using System.IO;
7using System.Linq;
8using System.Reflection;
9using System.Text;
10using System.Xml;
11using System.Xml.Serialization;
12
13namespace EmulatorLauncher.Common.FileFormats
14{
15public class XmlDeserializer
16{
17public static T DeserializeFile<T>(string xml) where T : class
18{
19var settings = new XmlReaderSettings
20{
21ConformanceLevel = ConformanceLevel.Auto,
22ValidationType = ValidationType.None
23};
24
25using (var reader = new FileStream(xml, FileMode.Open))
26{
27using (var xmlReader = XmlReader.Create(reader, settings))
28{
29var obj = DeserializeType(xmlReader, typeof(T));
30return (T)obj;
31}
32}
33}
34
35public static T DeserializeString<T>(string xml) where T : class
36{
37xml = XmlExtensions.StripInvalidXMLCharacters(xml);
38
39var settings = new XmlReaderSettings
40{
41ConformanceLevel = ConformanceLevel.Auto,
42ValidationType = ValidationType.None
43};
44
45using (var reader = new StringReader(xml))
46{
47using (var xmlReader = XmlReader.Create(reader, settings))
48{
49var obj = DeserializeType(xmlReader, typeof(T));
50return (T)obj;
51}
52}
53}
54
55#region Déserialisation
56private static object DeserializeType(XmlReader reader, Type t)
57{
58reader.SkipWhitespaces();
59
60if (t.IsValueType || t == typeof(string))
61return ReadValueType(reader, t);
62
63reader.MoveToContent();
64
65var attributeValues = new Dictionary<string, object>();
66if (reader.HasAttributes)
67{
68for (int i = 0; i < reader.AttributeCount; i++)
69{
70reader.MoveToNextAttribute();
71
72if (reader.LocalName == "type")
73{
74string xmlType = reader.Value;
75
76int ns = xmlType == null ? -1 : xmlType.IndexOf(":");
77if (ns > 0)
78xmlType = xmlType.Substring(ns + 1);
79
80XmlIncludeAttribute[] xmlIncludes = (XmlIncludeAttribute[])t.GetCustomAttributes(typeof(XmlIncludeAttribute), true);
81XmlIncludeAttribute xtype = xmlIncludes.FirstOrDefault(x => x.Type.Name.Equals(xmlType, StringComparison.InvariantCultureIgnoreCase));
82if (xtype != null)
83t = xtype.Type;
84}
85else
86attributeValues[reader.LocalName] = reader.Value;
87}
88}
89
90reader.MoveToElement();
91
92TypeMapping xmlMapping = GetTypeMapping(t);
93
94object ret = null;
95if (xmlMapping != null)
96{
97try { ret = xmlMapping.Constructor.Invoke(null); }
98catch { }
99}
100
101if (reader.IsEmptyElement)
102return ret;
103else
104{
105if (typeof(IXmlSerializable).IsAssignableFrom(t))
106{
107((IXmlSerializable)ret).ReadXml(reader);
108return ret;
109}
110
111reader.ReadStartElement();
112reader.SkipWhitespaces();
113}
114
115if (reader.NodeType == XmlNodeType.Text)
116{
117var mapping = xmlMapping.Properties.Select(p => p.Value).FirstOrDefault(p => p.XmlText != null);
118if (mapping != null)
119{
120object value = reader.Value;
121try { mapping.Property.SetValue(ret, value, null); }
122catch { }
123}
124
125reader.Read();
126reader.SkipWhitespaces();
127
128return ret;
129}
130
131var lists = new Dictionary<PropertyInfo, IList>();
132
133if (xmlMapping != null && ret != null)
134{
135foreach (var propertyMapping in xmlMapping.Properties.Values)
136{
137var property = propertyMapping.Property;
138if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string) &&
139property.PropertyType.IsGenericType && typeof(IList).IsAssignableFrom(property.PropertyType))
140{
141IList lst = property.GetValue(ret, null) as IList;
142if (lst == null && property.CanWrite)
143{
144lst = Activator.CreateInstance(property.PropertyType) as IList;
145property.SetValue(ret, lst, null);
146}
147
148if (lst != null)
149lists[property] = lst;
150}
151}
152}
153
154if (ret != null)
155{
156foreach (var attribute in attributeValues)
157{
158PropertyMapping propertyMapping = null;
159if (xmlMapping != null && xmlMapping.Properties.TryGetValue(attribute.Key, out propertyMapping))
160{
161try { propertyMapping.Property.SetValue(ret, attribute.Value, null); }
162catch { }
163}
164}
165}
166
167while (reader.IsStartElement())
168{
169var localName = reader.LocalName;
170
171if (reader.IsEmptyElement)
172{
173reader.Read();
174reader.SkipWhitespaces();
175continue;
176}
177
178PropertyMapping propertyMapping = null;
179if (ret != null && xmlMapping != null && xmlMapping.Properties.TryGetValue(localName, out propertyMapping))
180{
181var property = propertyMapping.Property;
182var propertyType = property.PropertyType;
183
184if (propertyType.IsValueType || propertyType == typeof(string))
185{
186if (propertyType.IsGenericType && propertyType.Name.StartsWith("Nullable"))
187propertyType = propertyType.GetGenericArguments().FirstOrDefault();
188
189if (propertyType != null)
190{
191object val = DeserializeType(reader, propertyType);
192if (val != null)
193property.SetValue(ret, val, null);
194
195reader.SkipWhitespaces();
196continue;
197}
198}
199else if (propertyType.IsGenericType && typeof(IList).IsAssignableFrom(propertyType))
200{
201IList lst;
202if (!lists.TryGetValue(property, out lst))
203{
204lst = Activator.CreateInstance(propertyType) as IList;
205lists[property] = lst;
206}
207
208Type tp = propertyType.GetGenericArguments().FirstOrDefault();
209
210if (propertyMapping.XmlElement == null)
211{
212string xmlTypeName = propertyMapping.Property.Name;
213
214if (propertyType.IsGenericType)
215{
216var genericType = propertyType.GetGenericArguments().FirstOrDefault();
217if (genericType != null)
218{
219if (propertyMapping.XmlArrayItem != null)
220xmlTypeName = propertyMapping.XmlArrayItem.ElementName;
221else
222{
223XmlTypeAttribute[] types = (XmlTypeAttribute[])genericType.GetCustomAttributes(typeof(XmlTypeAttribute), false);
224if (types.Length > 0)
225xmlTypeName = types[0].TypeName;
226else
227xmlTypeName = GetXmlTypeName(genericType);
228}
229}
230}
231
232if (reader.IsEmptyElement)
233reader.Read();
234else
235{
236reader.Read();
237reader.SkipWhitespaces();
238
239while (!reader.EOF && reader.NodeType == XmlNodeType.Element && reader.Name == xmlTypeName)
240{
241object value = DeserializeType(reader, tp);
242if (value != null)
243lst.Add(value);
244
245if (!reader.EOF)
246{
247if (reader.IsEmptyElement)
248reader.Read();
249else
250{
251if (reader.NodeType == XmlNodeType.Text)
252while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
253if (!reader.Read())
254break;
255
256if (reader.NodeType == XmlNodeType.EndElement)
257reader.ReadEndElement();
258}
259}
260
261reader.SkipWhitespaces();
262}
263
264if (propertyMapping.XmlArray != null)
265{
266if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == propertyMapping.XmlArray.ElementName)
267reader.ReadEndElement();
268}
269else
270{
271if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == propertyMapping.Property.Name)
272reader.ReadEndElement();
273}
274}
275
276reader.SkipWhitespaces();
277continue;
278}
279else
280{
281object value = null;
282
283if (tp == typeof(string) || tp.IsValueType)
284{
285if (!reader.EOF && !reader.IsEmptyElement)
286{
287value = ReadValueType(reader, tp);
288if (value != null)
289lst.Add(value);
290}
291
292reader.SkipWhitespaces();
293continue;
294}
295else
296value = DeserializeType(reader, tp);
297
298if (value != null)
299lst.Add(value);
300
301if (!reader.EOF && !reader.IsEmptyElement)
302{
303if (reader.NodeType == XmlNodeType.Text)
304while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
305if (!reader.Read())
306break;
307
308reader.ReadEndElement();
309}
310
311reader.SkipWhitespaces();
312
313if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement)
314{
315if (typeof(IEnumerable).IsAssignableFrom(propertyType) && (propertyType.IsGenericType || propertyType.IsArray))
316{
317string nameOfElement = string.IsNullOrEmpty(propertyMapping.XmlElement.ElementName) ? propertyMapping.Property.Name : propertyMapping.XmlElement.ElementName;
318if (reader.Name == nameOfElement)
319{
320reader.ReadEndElement();
321reader.SkipWhitespaces();
322}
323}
324}
325
326continue;
327}
328}
329else
330{
331Type xmlType = propertyType;
332
333bool convert = false;
334
335if (propertyMapping.XmlElement != null && propertyMapping.XmlElement.Type != null && propertyMapping.XmlElement.Type != xmlType)
336{
337convert = true;
338xmlType = propertyMapping.XmlElement.Type;
339}
340
341object value = DeserializeType(reader, xmlType);
342if (value != null)
343{
344if (convert)
345{
346var converter = xmlType.GetMethod("op_Implicit", new[] { xmlType });
347if (converter != null)
348value = converter.Invoke(null, new[] { value });
349}
350
351property.SetValue(ret, value, null);
352}
353}
354}
355else
356{
357XmlTypeAttribute[] types = (XmlTypeAttribute[])t.GetCustomAttributes(typeof(XmlTypeAttribute), false);
358if (types.Length > 0 && types[0].TypeName == localName)
359return DeserializeType(reader, t);
360
361XmlArrayAttribute[] arr = (XmlArrayAttribute[])t.GetCustomAttributes(typeof(XmlArrayAttribute), false);
362if (reader.IsEmptyElement)
363reader.Read();
364else
365reader.Skip();
366
367reader.SkipWhitespaces();
368continue;
369}
370
371if (!reader.EOF)
372{
373if (reader.IsEmptyElement)
374reader.Read();
375else
376{
377if (reader.NodeType == XmlNodeType.Text)
378while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
379if (!reader.Read())
380break;
381
382reader.ReadEndElement();
383}
384}
385reader.SkipWhitespaces();
386}
387
388return ret;
389}
390
391private static object ReadValueType(XmlReader reader, Type t)
392{
393if (!t.IsValueType && t != typeof(string))
394return null;
395
396if (reader.EOF)
397return null;
398
399if (reader.IsEmptyElement)
400{
401reader.Read();
402reader.SkipWhitespaces();
403return null;
404}
405
406try
407{
408if (t.IsEnum)
409{
410string value = reader.ReadElementContentAsString();
411return GetXmlEnumValue(t, value);
412}
413
414if (t == typeof(Int32))
415{
416string strValue = reader.ReadElementContentAsString();
417
418int value = 0;
419int.TryParse(strValue, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value);
420return value;
421}
422
423if (t == typeof(Decimal))
424{
425string strValue = reader.ReadElementContentAsString();
426
427decimal value = 0;
428decimal.TryParse(strValue, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value);
429return value;
430}
431
432try
433{
434object valueTypeValue = reader.ReadElementContentAs(t, null);
435reader.SkipWhitespaces();
436return valueTypeValue;
437}
438catch
439{
440return t == typeof(string) ? null : Activator.CreateInstance(t);
441}
442}
443catch
444{
445reader.Skip();
446reader.SkipWhitespaces();
447return null;
448}
449}
450
451private static object GetXmlEnumValue(System.Type value, string xmlEnum)
452{
453foreach (FieldInfo fi in value.GetFields())
454{
455var attributes = (XmlEnumAttribute[])fi.GetCustomAttributes(typeof(XmlEnumAttribute), false);
456if (attributes.Length > 0 && attributes[0].Name == xmlEnum)
457return fi.GetValue(fi.Name);
458
459if (fi.Name == xmlEnum)
460return fi.GetValue(fi.Name);
461}
462
463try { return Enum.Parse(value, xmlEnum); }
464catch { }
465
466return null;
467}
468
469private static string GetXmlTypeName(Type type)
470{
471switch (Type.GetTypeCode(type))
472{
473case TypeCode.Boolean:
474return "boolean";
475
476case TypeCode.SByte:
477return "byte";
478
479case TypeCode.Byte:
480return "unsignedByte";
481
482case TypeCode.Int16:
483return "short";
484
485case TypeCode.UInt16:
486return "unsignedShort";
487
488case TypeCode.Int32:
489return "int";
490
491case TypeCode.UInt32:
492return "unsignedInt";
493
494case TypeCode.Int64:
495return "long";
496
497case TypeCode.UInt64:
498return "unsignedLong";
499
500case TypeCode.Single:
501return "float";
502
503case TypeCode.Double:
504return "double";
505
506case TypeCode.Decimal:
507return "decimal";
508
509case TypeCode.DateTime:
510return "dateTime";
511
512case TypeCode.String:
513return "string";
514
515default:
516
517if (type == typeof(byte[]))
518return "base64Binary";
519
520if (type == typeof(Guid))
521return "guid";
522
523return type.Name;
524}
525}
526#endregion
527
528#region Cache
529class PropertyMapping
530{
531public PropertyInfo Property { get; set; }
532public XmlElementAttribute XmlElement { get; set; }
533public XmlArrayAttribute XmlArray { get; set; }
534public XmlArrayItemAttribute XmlArrayItem { get; set; }
535public XmlTextAttribute XmlText { get; set; }
536public XmlAttributeAttribute XmlAttribute { get; set; }
537}
538
539class TypeMapping
540{
541public TypeMapping()
542{
543Properties = new Dictionary<string, PropertyMapping>();
544}
545
546public ConstructorInfo Constructor { get; set; }
547public Dictionary<string, PropertyMapping> Properties { get; private set; }
548}
549
550static object _lock = new object();
551
552static TypeMapping GetTypeMapping(Type t)
553{
554lock (_lock)
555{
556TypeMapping xmlMapping;
557if (!_xmlTypeMappings.TryGetValue(t, out xmlMapping))
558{
559xmlMapping = new TypeMapping();
560xmlMapping.Constructor = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new Type[] { }, null);
561
562PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
563foreach (PropertyInfo property in properties)
564{
565if (Attribute.IsDefined(property, typeof(XmlIgnoreAttribute)))
566continue;
567
568if ((property.PropertyType.IsValueType || property.PropertyType == typeof(string)) && !property.CanWrite)
569continue;
570
571string name = property.Name;
572
573PropertyMapping pm = new PropertyMapping();
574pm.Property = property;
575
576if (Attribute.IsDefined(property, typeof(XmlElementAttribute)))
577{
578XmlElementAttribute xmlElement = property.GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute;
579if (xmlElement != null)
580{
581if (!string.IsNullOrEmpty(xmlElement.ElementName))
582name = xmlElement.ElementName;
583
584pm.XmlElement = xmlElement;
585}
586}
587else if (Attribute.IsDefined(property, typeof(XmlArrayAttribute)))
588{
589XmlArrayAttribute xmlArray = property.GetCustomAttributes(typeof(XmlArrayAttribute), true).FirstOrDefault() as XmlArrayAttribute;
590if (xmlArray != null)
591{
592if (!string.IsNullOrEmpty(xmlArray.ElementName))
593name = xmlArray.ElementName;
594
595pm.XmlArray = xmlArray;
596}
597}
598
599if (Attribute.IsDefined(property, typeof(XmlArrayItemAttribute)))
600{
601XmlArrayItemAttribute xmlArrayItem = property.GetCustomAttributes(typeof(XmlArrayItemAttribute), true).FirstOrDefault() as XmlArrayItemAttribute;
602if (xmlArrayItem != null)
603pm.XmlArrayItem = xmlArrayItem;
604}
605
606if (Attribute.IsDefined(property, typeof(XmlTextAttribute)))
607{
608XmlTextAttribute xmlText = property.GetCustomAttributes(typeof(XmlTextAttribute), true).FirstOrDefault() as XmlTextAttribute;
609if (xmlText != null)
610pm.XmlText = xmlText;
611}
612
613if (Attribute.IsDefined(property, typeof(XmlAttributeAttribute)))
614{
615XmlAttributeAttribute xmlAttribute = property.GetCustomAttributes(typeof(XmlAttributeAttribute), true).FirstOrDefault() as XmlAttributeAttribute;
616if (xmlAttribute != null)
617{
618name = xmlAttribute.AttributeName;
619pm.XmlAttribute = xmlAttribute;
620}
621}
622
623xmlMapping.Properties[name] = pm;
624}
625
626_xmlTypeMappings[t] = xmlMapping;
627}
628
629return xmlMapping;
630}
631}
632
633private static Dictionary<Type, TypeMapping> _xmlTypeMappings = new Dictionary<Type, TypeMapping>();
634#endregion
635}
636}
637