ProjectArcade

Форк
0
722 строки · 24.1 Кб
1
/*--------------------------------------------------------------------------
2
* DynamicJson
3
* ver 1.2.0.0 (May. 21th, 2010)
4
*
5
* created and maintained by neuecc <ils@neue.cc>
6
* licensed under Microsoft Public License(Ms-PL)
7
* 
8
* Modified by f.caruso
9
* 
10
*--------------------------------------------------------------------------*/
11
using System;
12
using System.Collections;
13
using System.Collections.Generic;
14
using System.Diagnostics;
15
using System.Dynamic;
16
using System.IO;
17
using System.Linq;
18
using System.Reflection;
19
using System.Runtime.Serialization.Json;
20
using System.Text;
21
using System.Xml;
22
using System.Xml.Linq;
23
using System.Globalization;
24

25
namespace EmulatorLauncher.Common.FileFormats
26
{
27
    public class DynamicJson : DynamicObject
28
    {        
29
        /// <summary>
30
        /// Loads a json string
31
        /// </summary>
32
        /// <param name="json"></param>
33
        /// <returns></returns>
34
        public static DynamicJson Parse(string json)
35
        {
36
            return Parse(json, Encoding.UTF8);
37
        }
38

39
        /// <summary>
40
        /// Loads a json string
41
        /// </summary>
42
        /// <param name="json"></param>
43
        /// <param name="encoding"></param>
44
        /// <returns></returns>
45
        public static DynamicJson Parse(string json, Encoding encoding)
46
        {
47
            using (var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max))
48
            {
49
                return ToValue(XElement.Load(reader));
50
            }
51
        }
52
        /// <summary>
53
        /// Loads a json file
54
        /// </summary>
55
        /// <param name="jsonFile"></param>
56
        /// <returns></returns>
57
        public static DynamicJson Load(string jsonFile)
58
        {
59
            if (!File.Exists(jsonFile))
60
                return new DynamicJson() { _path = jsonFile };
61

62
            var bytes = File.ReadAllText(jsonFile);
63
            DynamicJson ret = Parse(bytes, Encoding.UTF8);
64
            ret._path = jsonFile;
65
            return ret;
66
        }
67

68
        private string _path;
69

70
        public void Save()
71
        {
72
            if (!string.IsNullOrEmpty(_path))
73
                Save(_path);
74
        }
75

76
        public void Save(string ymlFile)
77
        {
78
            File.WriteAllText(ymlFile, ToString());
79
        }
80

81
        public DynamicJson GetOrCreateContainer(string key, bool asArray = false)
82
        {
83
            DynamicJson result;
84

85
            var element = GetElement(key);
86
            if (element == null)
87
            {
88
                XElement tempElement;                 
89
                XNamespace ns = "item";
90

91
                if (KeyHasInvalidXmlChars(key))
92
                    tempElement = new XElement(ns + "item", new XAttribute("item", key), CreateTypeAttr(asArray ? JsonType.array : JsonType.@object));
93
                else
94
                    tempElement = new XElement(key, CreateTypeAttr(asArray ? JsonType.array : JsonType.@object));
95

96
                result = new DynamicJson(tempElement, asArray ? JsonType.array : JsonType.@object) { _temporaryParentObject = this };
97
                return result;
98
            }
99

100
            object ret;
101
            if (TryGet(element, out ret) && ret is DynamicJson)
102
                return (DynamicJson)ret;
103

104
            return null;
105
        }
106

107
        public DynamicJson GetObject(string key)
108
        {
109
            var element = GetElement(key);
110
            if (element == null)
111
                return null;
112

113
            object ret;
114
            if (TryGet(element, out ret) && ret is DynamicJson)
115
                return ret as DynamicJson;
116

117
            return null;
118
        }
119

120
        public ArrayList GetArray(string key)
121
        {
122
            var element = GetElement(key);            
123
            if (element == null)
124
                return new ArrayList();
125

126
            object ret;
127
            if (TryGet(element, out ret) && ret is DynamicJson)
128
            {
129
                DynamicJson dj = ret as DynamicJson;
130
                if (dj.IsArray)
131
                {
132
                    var arr = new ArrayList();
133
                    
134
                    foreach (var item in dj.xml.Elements().Select(x => ToValue(x)))
135
                        arr.Add(item);
136

137
                    return arr;
138
                }
139
            }
140

141
            return new ArrayList();
142
        }
143

144
        public void SetObject(string key, object obj)
145
        {
146
            if (obj is IEnumerable && !(obj is string))
147
                SetArray(key, (IEnumerable) obj);
148
            else
149
                TrySet(key, obj);
150
        }
151

152
        private void SetArray(string key, IEnumerable array)
153
        {
154
            DynamicJson ret = new DynamicJson() { _temporaryParentObject = this };
155
            ret.jsonType = JsonType.array;
156

157
            foreach (var item in array)
158
                ret.xml.Add(new XElement("item", CreateTypeAttr(GetJsonType(item)), CreateJsonNode(item)));
159

160
            TrySet(key, ret);
161
        }
162

163
        static HashSet<char> InvalidChars = new HashSet<char>(new char[] 
164
        {
165
            '+', '&', '<', '>', '"', '\'', ' ', '\r', '\n', '!', '@', '#', '$', '%', '^', '*', '(', ')', '+', '=', '{', '}', '[', ']', '\\', '|', '/', '?', ',', ';', '~'
166
        });
167

168
        private bool KeyHasInvalidXmlChars(string key)
169
        {
170
            foreach (var ch in key)
171
                if (InvalidChars.Contains(ch))
172
                    return true;
173

174
            return false;
175
        }
176

177
        private XElement GetElement(string key)
178
        {
179
            XElement element = null;
180

181
            try
182
            {
183
                if (KeyHasInvalidXmlChars(key))
184
                    element = xml.Elements().FirstOrDefault(e => e.Name.LocalName == "item" && e.Attribute("item") != null && e.Attribute("item").Value == key);
185
                else
186
                    element = xml.Element(key);
187
            }
188
            catch
189
            {
190
                element = xml.Elements().FirstOrDefault(e => e.Name.LocalName == "item" && e.Attribute("item") != null && e.Attribute("item").Value == key);
191
            }
192

193
            return element;
194
        }
195

196
        public string this[string key]
197
        {
198
            get
199
            {
200
                var element = GetElement(key);                
201
                if (element != null)
202
                {
203
                    object ret;
204
                    if (TryGet(element, out ret))
205
                    {
206
                        DynamicJson child = ret as DynamicJson;
207
                        if (child == null || child.IsArray)
208
                            return ret.ToString();
209
                    }
210
                }
211

212
                return null;
213
            }
214
            set
215
            {
216
                object newValue = value;
217

218
                var element = GetElement(key);
219
                if (element != null)
220
                {
221
                    var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
222
                    switch (type)
223
                    {
224
                        case JsonType.boolean:
225
                            newValue = Convert.ToBoolean(value);
226
                            break;
227

228
                        case JsonType.number:
229
                            newValue = Convert.ToDouble(value, CultureInfo.InvariantCulture);
230
                            break;
231

232
                        case JsonType.array:
233
                        case JsonType.@object:
234
                            newValue = Parse(value);
235
                            break;
236
                    }
237
                }
238
                else if (!string.IsNullOrEmpty(value))
239
                {
240
                    // Guess type from data
241
                    if (value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
242
                        newValue = value.Substring(1, value.Length - 2);
243
                    else if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "false")
244
                        newValue = Convert.ToBoolean(value);
245
                    else if (value.All(c => char.IsNumber(c) || c == '.' || c == '-'))
246
                        newValue = Convert.ToDouble(value, CultureInfo.InvariantCulture);
247
                    else if (value.Length > 1 && value.StartsWith("[") && value.EndsWith("]")) // Array
248
                        newValue = Parse(value);
249
                }
250

251
                DynamicJson dj = newValue as DynamicJson;
252
                if (dj != null && dj.IsArray)
253
                {
254
                    var final = new ArrayList();
255

256
                    dynamic enu = dj;
257
                    foreach (var item in enu)
258
                        final.Add(item);
259

260
                    newValue = final.ToArray();
261
                }
262

263
                TrySet(key, newValue);                
264
            }
265
        }
266

267
        /// <summary>create JsonSring from primitive or IEnumerable or Object({public property name:property value})</summary>
268
        public static string Serialize(object obj)
269
        {
270
            return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));
271
        }
272

273
        private static dynamic ToValue(XElement element)
274
        {
275
            var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
276
            switch (type)
277
            {
278
                case JsonType.boolean:
279
                    return (bool)element;
280
                case JsonType.number:
281
                    return (double)element;
282
                case JsonType.@string:
283
                    return (string)element;
284
                case JsonType.@object:
285
                case JsonType.array:
286
                    return new DynamicJson(element, type);
287
                case JsonType.@null:
288
                default:
289
                    return null;
290
            }
291
        }
292

293
        private static JsonType GetJsonType(object obj)
294
        {
295
            if (obj == null) return JsonType.@null;
296

297
            switch (Type.GetTypeCode(obj.GetType()))
298
            {
299
                case TypeCode.Boolean:
300
                    return JsonType.boolean;
301
                case TypeCode.String:
302
                case TypeCode.Char:
303
                case TypeCode.DateTime:
304
                    return JsonType.@string;
305
                case TypeCode.Int16:
306
                case TypeCode.Int32:
307
                case TypeCode.Int64:
308
                case TypeCode.UInt16:
309
                case TypeCode.UInt32:
310
                case TypeCode.UInt64:
311
                case TypeCode.Single:
312
                case TypeCode.Double:
313
                case TypeCode.Decimal:
314
                case TypeCode.SByte:
315
                case TypeCode.Byte:
316
                    return JsonType.number;
317
                case TypeCode.Object:
318
                    if (obj is DynamicJson)
319
                        return ((DynamicJson)obj).IsArray ? JsonType.array : JsonType.@object;
320
                    else 
321
                        return (obj is IEnumerable) ? JsonType.array : JsonType.@object;
322
                case TypeCode.DBNull:
323
                case TypeCode.Empty:
324
                default:
325
                    return JsonType.@null;
326
            }
327
        }
328

329
        private static XAttribute CreateTypeAttr(JsonType type)
330
        {
331
            return new XAttribute("type", type.ToString());
332
        }
333

334
        private static object CreateJsonNode(object obj)
335
        {
336
            var type = GetJsonType(obj);
337
            switch (type)
338
            {
339
                case JsonType.@string:
340
                case JsonType.number:
341
                    return obj;
342
                case JsonType.boolean:
343
                    return obj.ToString().ToLower();
344
                case JsonType.@object:
345
                    return CreateXObject(obj);
346
                case JsonType.array:
347
                    if (obj is DynamicJson)
348
                        return CreateXObject(obj);
349

350
                    return CreateXArray(obj as IEnumerable);
351
                case JsonType.@null:
352
                default:
353
                    return null;
354
            }
355
        }
356

357
        private static IEnumerable<XStreamingElement> CreateXArray<T>(T obj) where T : IEnumerable
358
        {
359
            return obj.Cast<object>()
360
                .Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o)));
361
        }
362

363
        private static IEnumerable<XStreamingElement> CreateXObject(object obj)
364
        {
365
            DynamicJson dj = obj as DynamicJson;
366
            if (dj != null)
367
            {
368
                if (dj.IsArray)
369
                {
370
                    var ret = new List<XStreamingElement>();
371

372
                    foreach(var item in (dynamic)dj)
373
                        ret.Add(new XStreamingElement("item", CreateTypeAttr(GetJsonType(item)), CreateJsonNode(item)));
374

375
                    return ret.ToArray();
376
                }
377
                else
378
                {
379
                    return dj.GetDynamicMemberNames()
380
                        .Select(pi => new { Name = pi, Value = ToValue(dj.GetElement(pi)) })
381
                        .Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value)));
382
                }
383
            }
384

385
            return obj.GetType()
386
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
387
                .Select(pi => new { Name = pi.Name, Value = pi.GetValue(obj, null) })
388
                .Select(a => new XStreamingElement(a.Name, CreateTypeAttr(GetJsonType(a.Value)), CreateJsonNode(a.Value)));
389
        }
390

391
        private static string CreateJsonString(XStreamingElement element)
392
        {
393
            using (var ms = new MemoryStream())
394
            {
395
                var methods = typeof(JsonReaderWriterFactory).GetMethods(BindingFlags.Static | BindingFlags.Public);
396
                var createJsonWriterEx = methods.FirstOrDefault(method => method.Name == "CreateJsonWriter" && method.GetParameters().Length == 4);
397

398
                using (var writer = createJsonWriterEx == null ?
399
                    JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.UTF8, false) :
400
                    createJsonWriterEx.Invoke(null, new object[] { ms, Encoding.UTF8, false, true }) as System.Xml.XmlDictionaryWriter)
401
                {
402
                    element.WriteTo(writer);
403
                    writer.Flush();
404
                    return Encoding.UTF8.GetString(ms.ToArray());
405
                }
406
            }
407
        }
408

409
        // dynamic structure represents JavaScript Object/Array
410

411
        XElement xml;
412
        JsonType jsonType;
413

414
        /// <summary>create blank JSObject</summary>
415
        public DynamicJson()
416
        {
417
            xml = new XElement("root", CreateTypeAttr(JsonType.@object));
418
            jsonType = JsonType.@object;
419
        }
420

421
        private DynamicJson(XElement element, JsonType type)
422
        {
423
            Debug.Assert(type == JsonType.array || type == JsonType.@object);
424

425
            xml = element;
426
            jsonType = type;
427
        }
428

429
        public bool IsObject { get { return jsonType == JsonType.@object; } }
430

431
        public bool IsArray { get { return jsonType == JsonType.array; } }
432

433
        /// <summary>has property or not</summary>
434
        public bool IsDefined(string name)
435
        {
436
            return IsObject && (GetElement(name) != null);
437
        }
438

439
        /// <summary>has property or not</summary>
440
        public bool IsDefined(int index)
441
        {
442
            return IsArray && (xml.Elements().ElementAtOrDefault(index) != null);
443
        }
444

445
        /// <summary>delete property</summary>
446
        public bool Remove(string name)
447
        {
448
            var elem = GetElement(name);
449
            if (elem != null)
450
            {
451
                elem.Remove();
452
                return true;
453
            }
454
            else return false;
455
        }
456

457
        /// <summary>delete property</summary>
458
        public bool Remove(int index)
459
        {
460
            var elem = xml.Elements().ElementAtOrDefault(index);
461
            if (elem != null)
462
            {
463
                elem.Remove();
464
                return true;
465
            }
466
            else return false;
467
        }
468

469
        /// <summary>mapping to Array or Class by Public PropertyName</summary>
470
        public T Deserialize<T>()
471
        {
472
            return (T)Deserialize(typeof(T));
473
        }
474

475
        private object Deserialize(Type type)
476
        {
477
            return (IsArray) ? DeserializeArray(type) : DeserializeObject(type);
478
        }
479

480
        private dynamic DeserializeValue(XElement element, Type elementType)
481
        {
482
            var value = ToValue(element);
483
            if (value is DynamicJson)
484
            {
485
                value = ((DynamicJson)value).Deserialize(elementType);
486
            }
487
            return Convert.ChangeType(value, elementType);
488
        }
489

490
        private object DeserializeObject(Type targetType)
491
        {
492
            var result = Activator.CreateInstance(targetType);
493
            var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
494
                .Where(p => p.CanWrite)
495
                .ToDictionary(pi => pi.Name, pi => pi);
496
            foreach (var item in xml.Elements())
497
            {
498
                PropertyInfo propertyInfo;
499
                if (!dict.TryGetValue(item.Name.LocalName, out propertyInfo)) continue;
500
                var value = DeserializeValue(item, propertyInfo.PropertyType);
501
                propertyInfo.SetValue(result, value, null);
502
            }
503
            return result;
504
        }
505

506
        private object DeserializeArray(Type targetType)
507
        {
508
            if (targetType.IsArray) // Foo[]
509
            {
510
                var elemType = targetType.GetElementType();
511
                dynamic array = Array.CreateInstance(elemType, xml.Elements().Count());
512
                var index = 0;
513
                foreach (var item in xml.Elements())
514
                {
515
                    array[index++] = DeserializeValue(item, elemType);
516
                }
517
                return array;
518
            }
519
            else // List<Foo>
520
            {
521
                var elemType = targetType.GetGenericArguments()[0];
522
                dynamic list = Activator.CreateInstance(targetType);
523
                foreach (var item in xml.Elements())
524
                {
525
                    list.Add(DeserializeValue(item, elemType));
526
                }
527
                return list;
528
            }
529
        }
530

531
        // Delete
532
        public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
533
        {
534
            result = (IsArray)
535
                ? Remove((int)args[0])
536
                : Remove((string)args[0]);
537
            return true;
538
        }
539

540
        // IsDefined, if has args then TryGetMember
541
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
542
        {
543
            if (args.Length > 0)
544
            {
545
                result = null;
546
                return false;
547
            }
548

549
            result = IsDefined(binder.Name);
550
            return true;
551
        }
552

553
        // Deserialize or foreach(IEnumerable)
554
        public override bool TryConvert(ConvertBinder binder, out object result)
555
        {
556
            if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[]))
557
            {
558
                var ie = (IsArray)
559
                    ? xml.Elements().Select(x => ToValue(x))
560
                    : xml.Elements().Select(x => (dynamic)new KeyValuePair<string, object>(x.Name.LocalName, ToValue(x)));
561
                result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie;
562
            }
563
            else
564
            {
565
                result = Deserialize(binder.Type);
566
            }
567
            return true;
568
        }
569

570
        private bool TryGet(XElement element, out object result)
571
        {
572
            if (element == null)
573
            {
574
                result = null;
575
                return false;
576
            }
577

578
            result = ToValue(element);
579
            return true;
580
        }
581

582
        public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
583
        {
584
            return (IsArray)
585
                ? TryGet(xml.Elements().ElementAtOrDefault((int)indexes[0]), out result)
586
                : TryGet(GetElement((string)indexes[0]), out result);
587
        }
588

589
        public override bool TryGetMember(GetMemberBinder binder, out object result)
590
        {
591
            if (IsArray)
592
            {
593
                if (binder.Name == "Length")
594
                {
595
                    result = xml.Elements().Count();
596
                    return true;
597
                }
598

599
                return TryGet(xml.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result);
600
            }
601

602
            var element = GetElement(binder.Name);
603
            if (element == null)
604
            {
605
                XNamespace ns = "item";
606
                XElement tempElement;
607

608
                if (KeyHasInvalidXmlChars(binder.Name))
609
                    tempElement = new XElement(ns + "item", new XAttribute("item", binder.Name), CreateTypeAttr(JsonType.@object));
610
                else
611
                    tempElement = new XElement(binder.Name, CreateTypeAttr(JsonType.@object));
612

613
                result = new DynamicJson(tempElement, JsonType.@object) { _temporaryParentObject = this };
614
                return true;
615
            }
616

617
            return TryGet(element, out result);
618
        }
619

620
        private DynamicJson _temporaryParentObject;
621

622
        private void AssignTemporaryParentObject()
623
        {
624
            if (_temporaryParentObject != null)
625
            {
626
                _temporaryParentObject.xml.Add(xml);
627

628
                _temporaryParentObject.AssignTemporaryParentObject();
629
                _temporaryParentObject = null;
630
            }
631
        }
632

633
        private bool TrySet(string name, object value)
634
        {
635
            AssignTemporaryParentObject();
636

637
            var type = GetJsonType(value);
638
            var element = GetElement(name);
639
            if (element == null)
640
            {
641
                XNamespace ns = "item";
642

643
                try
644
                {
645
                    if (KeyHasInvalidXmlChars(name))
646
                        xml.Add(new XElement(ns + "item", new XAttribute("item", name), CreateTypeAttr(type), CreateJsonNode(value)));
647
                    else
648
                        xml.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value)));
649
                }
650
                catch 
651
                {
652
                    xml.Add(new XElement(ns + "item", new XAttribute("item", name), CreateTypeAttr(type), CreateJsonNode(value)));
653
                }
654
            }
655
            else
656
            {
657
                element.Attribute("type").Value = type.ToString();
658
                element.ReplaceNodes(CreateJsonNode(value));
659
            }
660

661
            return true;
662
        }
663

664
        private bool TrySet(int index, object value)
665
        {
666
            AssignTemporaryParentObject();
667

668
            var type = GetJsonType(value);
669
            var e = xml.Elements().ElementAtOrDefault(index);
670
            if (e == null)
671
            {
672
                xml.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value)));
673
            }
674
            else
675
            {
676
                e.Attribute("type").Value = type.ToString();
677
                e.ReplaceNodes(CreateJsonNode(value));
678
            }
679

680
            return true;
681
        }
682

683
        public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
684
        {
685
            return (IsArray)
686
                ? TrySet((int)indexes[0], value)
687
                : TrySet((string)indexes[0], value);
688
        }
689

690
        public override bool TrySetMember(SetMemberBinder binder, object value)
691
        {
692
            return (IsArray)
693
                ? TrySet(int.Parse(binder.Name), value)
694
                : TrySet(binder.Name, value);
695
        }
696

697
        public override IEnumerable<string> GetDynamicMemberNames()
698
        {
699
            return (IsArray)
700
                ? xml.Elements().Select((x, i) => i.ToString())
701
                : xml.Elements().Select(x => x.Name.LocalName);
702
        }
703

704
        /// <summary>Serialize to JsonString</summary>
705
        public override string ToString()
706
        {
707
            // <foo type="null"></foo> is can't serialize. replace to <foo type="null" />
708
            foreach (var elem in xml.Descendants().Where(x => x.Attribute("type").Value == "null"))
709
            {
710
                elem.RemoveNodes();
711
            }
712
            return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), xml.Elements()));
713
        }
714
        
715
        enum JsonType
716
        {
717
            @string, number, boolean, @object, array, @null
718
        }
719
    }
720

721

722
}

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

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

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

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