ProjectArcade

Форк
0
636 строк · 24.2 Кб
1
using System;
2
using System.Collections;
3
using System.Collections.Generic;
4
using System.Diagnostics;
5
using System.Globalization;
6
using System.IO;
7
using System.Linq;
8
using System.Reflection;
9
using System.Text;
10
using System.Xml;
11
using System.Xml.Serialization;
12

13
namespace EmulatorLauncher.Common.FileFormats
14
{
15
    public class XmlDeserializer
16
    {
17
        public static T DeserializeFile<T>(string xml) where T : class
18
        {
19
            var settings = new XmlReaderSettings
20
            {
21
                ConformanceLevel = ConformanceLevel.Auto,
22
                ValidationType = ValidationType.None
23
            };
24

25
            using (var reader = new FileStream(xml, FileMode.Open))
26
            {
27
                using (var xmlReader = XmlReader.Create(reader, settings))
28
                {
29
                    var obj = DeserializeType(xmlReader, typeof(T));
30
                    return (T)obj;
31
                }
32
            }
33
        }
34

35
        public static T DeserializeString<T>(string xml) where T : class
36
        {
37
            xml = XmlExtensions.StripInvalidXMLCharacters(xml);
38

39
            var settings = new XmlReaderSettings
40
            {
41
                ConformanceLevel = ConformanceLevel.Auto,
42
                ValidationType = ValidationType.None
43
            };
44

45
            using (var reader = new StringReader(xml))
46
            {
47
                using (var xmlReader = XmlReader.Create(reader, settings))
48
                {
49
                    var obj = DeserializeType(xmlReader, typeof(T));
50
                    return (T)obj;
51
                }
52
            }
53
        }
54

55
        #region Déserialisation
56
        private static object DeserializeType(XmlReader reader, Type t)
57
        {
58
            reader.SkipWhitespaces();
59

60
            if (t.IsValueType || t == typeof(string))
61
                return ReadValueType(reader, t);
62

63
            reader.MoveToContent();
64

65
            var attributeValues = new Dictionary<string, object>();
66
            if (reader.HasAttributes)
67
            {
68
                for (int i = 0; i < reader.AttributeCount; i++)
69
                {
70
                    reader.MoveToNextAttribute();
71

72
                    if (reader.LocalName == "type")
73
                    {
74
                        string xmlType = reader.Value;
75

76
                        int ns = xmlType == null ? -1 : xmlType.IndexOf(":");
77
                        if (ns > 0)
78
                            xmlType = xmlType.Substring(ns + 1);
79

80
                        XmlIncludeAttribute[] xmlIncludes = (XmlIncludeAttribute[])t.GetCustomAttributes(typeof(XmlIncludeAttribute), true);
81
                        XmlIncludeAttribute xtype = xmlIncludes.FirstOrDefault(x => x.Type.Name.Equals(xmlType, StringComparison.InvariantCultureIgnoreCase));
82
                        if (xtype != null)
83
                            t = xtype.Type;
84
                    }
85
                    else
86
                        attributeValues[reader.LocalName] = reader.Value;
87
                }
88
            }
89

90
            reader.MoveToElement();
91

92
            TypeMapping xmlMapping = GetTypeMapping(t);
93

94
            object ret = null;
95
            if (xmlMapping != null)
96
            {
97
                try { ret = xmlMapping.Constructor.Invoke(null); }
98
                catch { }
99
            }
100

101
            if (reader.IsEmptyElement)
102
                return ret;
103
            else
104
            {
105
                if (typeof(IXmlSerializable).IsAssignableFrom(t))
106
                {
107
                    ((IXmlSerializable)ret).ReadXml(reader);
108
                    return ret;
109
                }
110

111
                reader.ReadStartElement();
112
                reader.SkipWhitespaces();
113
            }
114

115
            if (reader.NodeType == XmlNodeType.Text)
116
            {
117
                var mapping = xmlMapping.Properties.Select(p => p.Value).FirstOrDefault(p => p.XmlText != null);
118
                if (mapping != null)
119
                {
120
                    object value = reader.Value;
121
                    try { mapping.Property.SetValue(ret, value, null); }
122
                    catch { }
123
                }
124

125
                reader.Read();
126
                reader.SkipWhitespaces();
127

128
                return ret;
129
            }
130

131
            var lists = new Dictionary<PropertyInfo, IList>();
132

133
            if (xmlMapping != null && ret != null)
134
            {
135
                foreach (var propertyMapping in xmlMapping.Properties.Values)
136
                {
137
                    var property = propertyMapping.Property;
138
                    if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string) &&
139
                        property.PropertyType.IsGenericType && typeof(IList).IsAssignableFrom(property.PropertyType))
140
                    {
141
                        IList lst = property.GetValue(ret, null) as IList;
142
                        if (lst == null && property.CanWrite)
143
                        {
144
                            lst = Activator.CreateInstance(property.PropertyType) as IList;
145
                            property.SetValue(ret, lst, null);
146
                        }
147

148
                        if (lst != null)
149
                            lists[property] = lst;
150
                    }
151
                }
152
            }
153

154
            if (ret != null)
155
            {
156
                foreach (var attribute in attributeValues)
157
                {
158
                    PropertyMapping propertyMapping = null;
159
                    if (xmlMapping != null && xmlMapping.Properties.TryGetValue(attribute.Key, out propertyMapping))
160
                    {
161
                        try { propertyMapping.Property.SetValue(ret, attribute.Value, null); }
162
                        catch { }
163
                    }
164
                }
165
            }
166

167
            while (reader.IsStartElement())
168
            {
169
                var localName = reader.LocalName;
170

171
                if (reader.IsEmptyElement)
172
                {
173
                    reader.Read();
174
                    reader.SkipWhitespaces();
175
                    continue;
176
                }
177

178
                PropertyMapping propertyMapping = null;
179
                if (ret != null && xmlMapping != null && xmlMapping.Properties.TryGetValue(localName, out propertyMapping))
180
                {
181
                    var property = propertyMapping.Property;
182
                    var propertyType = property.PropertyType;
183

184
                    if (propertyType.IsValueType || propertyType == typeof(string))
185
                    {
186
                        if (propertyType.IsGenericType && propertyType.Name.StartsWith("Nullable"))
187
                            propertyType = propertyType.GetGenericArguments().FirstOrDefault();
188

189
                        if (propertyType != null)
190
                        {
191
                            object val = DeserializeType(reader, propertyType);
192
                            if (val != null)
193
                                property.SetValue(ret, val, null);
194

195
                            reader.SkipWhitespaces();
196
                            continue;
197
                        }
198
                    }
199
                    else if (propertyType.IsGenericType && typeof(IList).IsAssignableFrom(propertyType))
200
                    {
201
                        IList lst;
202
                        if (!lists.TryGetValue(property, out lst))
203
                        {
204
                            lst = Activator.CreateInstance(propertyType) as IList;
205
                            lists[property] = lst;
206
                        }
207

208
                        Type tp = propertyType.GetGenericArguments().FirstOrDefault();
209

210
                        if (propertyMapping.XmlElement == null)
211
                        {
212
                            string xmlTypeName = propertyMapping.Property.Name;
213

214
                            if (propertyType.IsGenericType)
215
                            {
216
                                var genericType = propertyType.GetGenericArguments().FirstOrDefault();
217
                                if (genericType != null)
218
                                {
219
                                    if (propertyMapping.XmlArrayItem != null)
220
                                        xmlTypeName = propertyMapping.XmlArrayItem.ElementName;
221
                                    else
222
                                    {
223
                                        XmlTypeAttribute[] types = (XmlTypeAttribute[])genericType.GetCustomAttributes(typeof(XmlTypeAttribute), false);
224
                                        if (types.Length > 0)
225
                                            xmlTypeName = types[0].TypeName;
226
                                        else
227
                                            xmlTypeName = GetXmlTypeName(genericType);
228
                                    }
229
                                }
230
                            }
231

232
                            if (reader.IsEmptyElement)
233
                                reader.Read();
234
                            else
235
                            {
236
                                reader.Read();
237
                                reader.SkipWhitespaces();
238

239
                                while (!reader.EOF && reader.NodeType == XmlNodeType.Element && reader.Name == xmlTypeName)
240
                                {
241
                                    object value = DeserializeType(reader, tp);
242
                                    if (value != null)
243
                                        lst.Add(value);
244

245
                                    if (!reader.EOF)
246
                                    {
247
                                        if (reader.IsEmptyElement)
248
                                            reader.Read();
249
                                        else
250
                                        {
251
                                            if (reader.NodeType == XmlNodeType.Text)
252
                                                while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
253
                                                    if (!reader.Read())
254
                                                        break;
255

256
                                            if (reader.NodeType == XmlNodeType.EndElement)
257
                                                reader.ReadEndElement();
258
                                        }
259
                                    }
260

261
                                    reader.SkipWhitespaces();
262
                                }
263

264
                                if (propertyMapping.XmlArray != null)
265
                                {
266
                                    if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == propertyMapping.XmlArray.ElementName)
267
                                        reader.ReadEndElement();
268
                                }
269
                                else
270
                                {
271
                                    if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement && reader.Name == propertyMapping.Property.Name)
272
                                        reader.ReadEndElement();
273
                                }
274
                            }
275

276
                            reader.SkipWhitespaces();
277
                            continue;
278
                        }
279
                        else
280
                        {
281
                            object value = null;
282

283
                            if (tp == typeof(string) || tp.IsValueType)
284
                            {
285
                                if (!reader.EOF && !reader.IsEmptyElement)
286
                                {
287
                                    value = ReadValueType(reader, tp);
288
                                    if (value != null)
289
                                        lst.Add(value);
290
                                }
291

292
                                reader.SkipWhitespaces();
293
                                continue;
294
                            }
295
                            else
296
                                value = DeserializeType(reader, tp);
297

298
                            if (value != null)
299
                                lst.Add(value);
300

301
                            if (!reader.EOF && !reader.IsEmptyElement)
302
                            {
303
                                if (reader.NodeType == XmlNodeType.Text)
304
                                    while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
305
                                        if (!reader.Read())
306
                                            break;
307

308
                                reader.ReadEndElement();
309
                            }
310

311
                            reader.SkipWhitespaces();
312

313
                            if (!reader.EOF && reader.NodeType == XmlNodeType.EndElement)
314
                            {
315
                                if (typeof(IEnumerable).IsAssignableFrom(propertyType) && (propertyType.IsGenericType || propertyType.IsArray))
316
                                {
317
                                    string nameOfElement = string.IsNullOrEmpty(propertyMapping.XmlElement.ElementName) ? propertyMapping.Property.Name : propertyMapping.XmlElement.ElementName;
318
                                    if (reader.Name == nameOfElement)
319
                                    {
320
                                        reader.ReadEndElement();
321
                                        reader.SkipWhitespaces();
322
                                    }
323
                                }
324
                            }
325

326
                            continue;
327
                        }
328
                    }
329
                    else
330
                    {
331
                        Type xmlType = propertyType;
332

333
                        bool convert = false;
334

335
                        if (propertyMapping.XmlElement != null && propertyMapping.XmlElement.Type != null && propertyMapping.XmlElement.Type != xmlType)
336
                        {
337
                            convert = true;
338
                            xmlType = propertyMapping.XmlElement.Type;
339
                        }
340

341
                        object value = DeserializeType(reader, xmlType);
342
                        if (value != null)
343
                        {
344
                            if (convert)
345
                            {
346
                                var converter = xmlType.GetMethod("op_Implicit", new[] { xmlType });
347
                                if (converter != null)
348
                                    value = converter.Invoke(null, new[] { value });
349
                            }
350

351
                            property.SetValue(ret, value, null);
352
                        }
353
                    }
354
                }
355
                else
356
                {
357
                    XmlTypeAttribute[] types = (XmlTypeAttribute[])t.GetCustomAttributes(typeof(XmlTypeAttribute), false);
358
                    if (types.Length > 0 && types[0].TypeName == localName)
359
                        return DeserializeType(reader, t);
360

361
                    XmlArrayAttribute[] arr = (XmlArrayAttribute[])t.GetCustomAttributes(typeof(XmlArrayAttribute), false);
362
                    if (reader.IsEmptyElement)
363
                        reader.Read();
364
                    else
365
                        reader.Skip();
366

367
                    reader.SkipWhitespaces();
368
                    continue;
369
                }
370

371
                if (!reader.EOF)
372
                {
373
                    if (reader.IsEmptyElement)
374
                        reader.Read();
375
                    else
376
                    {
377
                        if (reader.NodeType == XmlNodeType.Text)
378
                            while (!reader.EOF && reader.NodeType != XmlNodeType.EndElement)
379
                                if (!reader.Read())
380
                                    break;
381

382
                        reader.ReadEndElement();
383
                    }
384
                }
385
                reader.SkipWhitespaces();
386
            }
387

388
            return ret;
389
        }
390

391
        private static object ReadValueType(XmlReader reader, Type t)
392
        {
393
            if (!t.IsValueType && t != typeof(string))
394
                return null;
395

396
            if (reader.EOF)
397
                return null;
398

399
            if (reader.IsEmptyElement)
400
            {
401
                reader.Read();
402
                reader.SkipWhitespaces();
403
                return null;
404
            }
405

406
            try
407
            {
408
                if (t.IsEnum)
409
                {
410
                    string value = reader.ReadElementContentAsString();
411
                    return GetXmlEnumValue(t, value);
412
                }
413

414
                if (t == typeof(Int32))
415
                {
416
                    string strValue = reader.ReadElementContentAsString();
417

418
                    int value = 0;
419
                    int.TryParse(strValue, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value);
420
                    return value;
421
                }
422

423
                if (t == typeof(Decimal))
424
                {
425
                    string strValue = reader.ReadElementContentAsString();
426

427
                    decimal value = 0;
428
                    decimal.TryParse(strValue, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out value);
429
                    return value;
430
                }
431

432
                try
433
                {
434
                    object valueTypeValue = reader.ReadElementContentAs(t, null);
435
                    reader.SkipWhitespaces();
436
                    return valueTypeValue;
437
                }
438
                catch
439
                {
440
                    return t == typeof(string) ? null : Activator.CreateInstance(t);
441
                }
442
            }
443
            catch
444
            {
445
                reader.Skip();
446
                reader.SkipWhitespaces();
447
                return null;
448
            }
449
        }
450

451
        private static object GetXmlEnumValue(System.Type value, string xmlEnum)
452
        {
453
            foreach (FieldInfo fi in value.GetFields())
454
            {
455
                var attributes = (XmlEnumAttribute[])fi.GetCustomAttributes(typeof(XmlEnumAttribute), false);
456
                if (attributes.Length > 0 && attributes[0].Name == xmlEnum)
457
                    return fi.GetValue(fi.Name);
458

459
                if (fi.Name == xmlEnum)
460
                    return fi.GetValue(fi.Name);
461
            }
462

463
            try { return Enum.Parse(value, xmlEnum); }
464
            catch { }
465

466
            return null;
467
        }
468

469
        private static string GetXmlTypeName(Type type)
470
        {
471
            switch (Type.GetTypeCode(type))
472
            {
473
                case TypeCode.Boolean:
474
                    return "boolean";
475

476
                case TypeCode.SByte:
477
                    return "byte";
478

479
                case TypeCode.Byte:
480
                    return "unsignedByte";
481

482
                case TypeCode.Int16:
483
                    return "short";
484

485
                case TypeCode.UInt16:
486
                    return "unsignedShort";
487

488
                case TypeCode.Int32:
489
                    return "int";
490

491
                case TypeCode.UInt32:
492
                    return "unsignedInt";
493

494
                case TypeCode.Int64:
495
                    return "long";
496

497
                case TypeCode.UInt64:
498
                    return "unsignedLong";
499

500
                case TypeCode.Single:
501
                    return "float";
502

503
                case TypeCode.Double:
504
                    return "double";
505

506
                case TypeCode.Decimal:
507
                    return "decimal";
508

509
                case TypeCode.DateTime:
510
                    return "dateTime";
511

512
                case TypeCode.String:
513
                    return "string";
514

515
                default:
516

517
                    if (type == typeof(byte[]))
518
                        return "base64Binary";
519

520
                    if (type == typeof(Guid))
521
                        return "guid";
522

523
                    return type.Name;
524
            }
525
        }
526
        #endregion
527

528
        #region Cache
529
        class PropertyMapping
530
        {
531
            public PropertyInfo Property { get; set; }
532
            public XmlElementAttribute XmlElement { get; set; }
533
            public XmlArrayAttribute XmlArray { get; set; }
534
            public XmlArrayItemAttribute XmlArrayItem { get; set; }
535
            public XmlTextAttribute XmlText { get; set; }
536
            public XmlAttributeAttribute XmlAttribute { get; set; }
537
        }
538

539
        class TypeMapping
540
        {
541
            public TypeMapping()
542
            {
543
                Properties = new Dictionary<string, PropertyMapping>();
544
            }
545

546
            public ConstructorInfo Constructor { get; set; }
547
            public Dictionary<string, PropertyMapping> Properties { get; private set; }
548
        }
549

550
        static object _lock = new object();
551

552
        static TypeMapping GetTypeMapping(Type t)
553
        {
554
            lock (_lock)
555
            {
556
                TypeMapping xmlMapping;
557
                if (!_xmlTypeMappings.TryGetValue(t, out xmlMapping))
558
                {
559
                    xmlMapping = new TypeMapping();
560
                    xmlMapping.Constructor = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, new Type[] { }, null);
561

562
                    PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
563
                    foreach (PropertyInfo property in properties)
564
                    {
565
                        if (Attribute.IsDefined(property, typeof(XmlIgnoreAttribute)))
566
                            continue;
567

568
                        if ((property.PropertyType.IsValueType || property.PropertyType == typeof(string)) && !property.CanWrite)
569
                            continue;
570

571
                        string name = property.Name;
572

573
                        PropertyMapping pm = new PropertyMapping();
574
                        pm.Property = property;
575

576
                        if (Attribute.IsDefined(property, typeof(XmlElementAttribute)))
577
                        {
578
                            XmlElementAttribute xmlElement = property.GetCustomAttributes(typeof(XmlElementAttribute), true).FirstOrDefault() as XmlElementAttribute;
579
                            if (xmlElement != null)
580
                            {
581
                                if (!string.IsNullOrEmpty(xmlElement.ElementName))
582
                                    name = xmlElement.ElementName;
583

584
                                pm.XmlElement = xmlElement;
585
                            }
586
                        }
587
                        else if (Attribute.IsDefined(property, typeof(XmlArrayAttribute)))
588
                        {
589
                            XmlArrayAttribute xmlArray = property.GetCustomAttributes(typeof(XmlArrayAttribute), true).FirstOrDefault() as XmlArrayAttribute;
590
                            if (xmlArray != null)
591
                            {
592
                                if (!string.IsNullOrEmpty(xmlArray.ElementName))
593
                                    name = xmlArray.ElementName;
594

595
                                pm.XmlArray = xmlArray;
596
                            }
597
                        }
598

599
                        if (Attribute.IsDefined(property, typeof(XmlArrayItemAttribute)))
600
                        {
601
                            XmlArrayItemAttribute xmlArrayItem = property.GetCustomAttributes(typeof(XmlArrayItemAttribute), true).FirstOrDefault() as XmlArrayItemAttribute;
602
                            if (xmlArrayItem != null)
603
                                pm.XmlArrayItem = xmlArrayItem;
604
                        }
605

606
                        if (Attribute.IsDefined(property, typeof(XmlTextAttribute)))
607
                        {
608
                            XmlTextAttribute xmlText = property.GetCustomAttributes(typeof(XmlTextAttribute), true).FirstOrDefault() as XmlTextAttribute;
609
                            if (xmlText != null)
610
                                pm.XmlText = xmlText;
611
                        }
612

613
                        if (Attribute.IsDefined(property, typeof(XmlAttributeAttribute)))
614
                        {
615
                            XmlAttributeAttribute xmlAttribute = property.GetCustomAttributes(typeof(XmlAttributeAttribute), true).FirstOrDefault() as XmlAttributeAttribute;
616
                            if (xmlAttribute != null)
617
                            {
618
                                name = xmlAttribute.AttributeName;
619
                                pm.XmlAttribute = xmlAttribute;
620
                            }
621
                        }
622

623
                        xmlMapping.Properties[name] = pm;
624
                    }
625

626
                    _xmlTypeMappings[t] = xmlMapping;
627
                }
628

629
                return xmlMapping;
630
            }
631
        }
632

633
        private static Dictionary<Type, TypeMapping> _xmlTypeMappings = new Dictionary<Type, TypeMapping>();
634
        #endregion
635
    }
636
}
637

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.