ProjectArcade
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*--------------------------------------------------------------------------*/
11using System;
12using System.Collections;
13using System.Collections.Generic;
14using System.Diagnostics;
15using System.Dynamic;
16using System.IO;
17using System.Linq;
18using System.Reflection;
19using System.Runtime.Serialization.Json;
20using System.Text;
21using System.Xml;
22using System.Xml.Linq;
23using System.Globalization;
24
25namespace EmulatorLauncher.Common.FileFormats
26{
27public class DynamicJson : DynamicObject
28{
29/// <summary>
30/// Loads a json string
31/// </summary>
32/// <param name="json"></param>
33/// <returns></returns>
34public static DynamicJson Parse(string json)
35{
36return 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>
45public static DynamicJson Parse(string json, Encoding encoding)
46{
47using (var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max))
48{
49return ToValue(XElement.Load(reader));
50}
51}
52/// <summary>
53/// Loads a json file
54/// </summary>
55/// <param name="jsonFile"></param>
56/// <returns></returns>
57public static DynamicJson Load(string jsonFile)
58{
59if (!File.Exists(jsonFile))
60return new DynamicJson() { _path = jsonFile };
61
62var bytes = File.ReadAllText(jsonFile);
63DynamicJson ret = Parse(bytes, Encoding.UTF8);
64ret._path = jsonFile;
65return ret;
66}
67
68private string _path;
69
70public void Save()
71{
72if (!string.IsNullOrEmpty(_path))
73Save(_path);
74}
75
76public void Save(string ymlFile)
77{
78File.WriteAllText(ymlFile, ToString());
79}
80
81public DynamicJson GetOrCreateContainer(string key, bool asArray = false)
82{
83DynamicJson result;
84
85var element = GetElement(key);
86if (element == null)
87{
88XElement tempElement;
89XNamespace ns = "item";
90
91if (KeyHasInvalidXmlChars(key))
92tempElement = new XElement(ns + "item", new XAttribute("item", key), CreateTypeAttr(asArray ? JsonType.array : JsonType.@object));
93else
94tempElement = new XElement(key, CreateTypeAttr(asArray ? JsonType.array : JsonType.@object));
95
96result = new DynamicJson(tempElement, asArray ? JsonType.array : JsonType.@object) { _temporaryParentObject = this };
97return result;
98}
99
100object ret;
101if (TryGet(element, out ret) && ret is DynamicJson)
102return (DynamicJson)ret;
103
104return null;
105}
106
107public DynamicJson GetObject(string key)
108{
109var element = GetElement(key);
110if (element == null)
111return null;
112
113object ret;
114if (TryGet(element, out ret) && ret is DynamicJson)
115return ret as DynamicJson;
116
117return null;
118}
119
120public ArrayList GetArray(string key)
121{
122var element = GetElement(key);
123if (element == null)
124return new ArrayList();
125
126object ret;
127if (TryGet(element, out ret) && ret is DynamicJson)
128{
129DynamicJson dj = ret as DynamicJson;
130if (dj.IsArray)
131{
132var arr = new ArrayList();
133
134foreach (var item in dj.xml.Elements().Select(x => ToValue(x)))
135arr.Add(item);
136
137return arr;
138}
139}
140
141return new ArrayList();
142}
143
144public void SetObject(string key, object obj)
145{
146if (obj is IEnumerable && !(obj is string))
147SetArray(key, (IEnumerable) obj);
148else
149TrySet(key, obj);
150}
151
152private void SetArray(string key, IEnumerable array)
153{
154DynamicJson ret = new DynamicJson() { _temporaryParentObject = this };
155ret.jsonType = JsonType.array;
156
157foreach (var item in array)
158ret.xml.Add(new XElement("item", CreateTypeAttr(GetJsonType(item)), CreateJsonNode(item)));
159
160TrySet(key, ret);
161}
162
163static HashSet<char> InvalidChars = new HashSet<char>(new char[]
164{
165'+', '&', '<', '>', '"', '\'', ' ', '\r', '\n', '!', '@', '#', '$', '%', '^', '*', '(', ')', '+', '=', '{', '}', '[', ']', '\\', '|', '/', '?', ',', ';', '~'
166});
167
168private bool KeyHasInvalidXmlChars(string key)
169{
170foreach (var ch in key)
171if (InvalidChars.Contains(ch))
172return true;
173
174return false;
175}
176
177private XElement GetElement(string key)
178{
179XElement element = null;
180
181try
182{
183if (KeyHasInvalidXmlChars(key))
184element = xml.Elements().FirstOrDefault(e => e.Name.LocalName == "item" && e.Attribute("item") != null && e.Attribute("item").Value == key);
185else
186element = xml.Element(key);
187}
188catch
189{
190element = xml.Elements().FirstOrDefault(e => e.Name.LocalName == "item" && e.Attribute("item") != null && e.Attribute("item").Value == key);
191}
192
193return element;
194}
195
196public string this[string key]
197{
198get
199{
200var element = GetElement(key);
201if (element != null)
202{
203object ret;
204if (TryGet(element, out ret))
205{
206DynamicJson child = ret as DynamicJson;
207if (child == null || child.IsArray)
208return ret.ToString();
209}
210}
211
212return null;
213}
214set
215{
216object newValue = value;
217
218var element = GetElement(key);
219if (element != null)
220{
221var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
222switch (type)
223{
224case JsonType.boolean:
225newValue = Convert.ToBoolean(value);
226break;
227
228case JsonType.number:
229newValue = Convert.ToDouble(value, CultureInfo.InvariantCulture);
230break;
231
232case JsonType.array:
233case JsonType.@object:
234newValue = Parse(value);
235break;
236}
237}
238else if (!string.IsNullOrEmpty(value))
239{
240// Guess type from data
241if (value.Length > 1 && value.StartsWith("\"") && value.EndsWith("\""))
242newValue = value.Substring(1, value.Length - 2);
243else if (value.ToLowerInvariant() == "true" || value.ToLowerInvariant() == "false")
244newValue = Convert.ToBoolean(value);
245else if (value.All(c => char.IsNumber(c) || c == '.' || c == '-'))
246newValue = Convert.ToDouble(value, CultureInfo.InvariantCulture);
247else if (value.Length > 1 && value.StartsWith("[") && value.EndsWith("]")) // Array
248newValue = Parse(value);
249}
250
251DynamicJson dj = newValue as DynamicJson;
252if (dj != null && dj.IsArray)
253{
254var final = new ArrayList();
255
256dynamic enu = dj;
257foreach (var item in enu)
258final.Add(item);
259
260newValue = final.ToArray();
261}
262
263TrySet(key, newValue);
264}
265}
266
267/// <summary>create JsonSring from primitive or IEnumerable or Object({public property name:property value})</summary>
268public static string Serialize(object obj)
269{
270return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));
271}
272
273private static dynamic ToValue(XElement element)
274{
275var type = (JsonType)Enum.Parse(typeof(JsonType), element.Attribute("type").Value);
276switch (type)
277{
278case JsonType.boolean:
279return (bool)element;
280case JsonType.number:
281return (double)element;
282case JsonType.@string:
283return (string)element;
284case JsonType.@object:
285case JsonType.array:
286return new DynamicJson(element, type);
287case JsonType.@null:
288default:
289return null;
290}
291}
292
293private static JsonType GetJsonType(object obj)
294{
295if (obj == null) return JsonType.@null;
296
297switch (Type.GetTypeCode(obj.GetType()))
298{
299case TypeCode.Boolean:
300return JsonType.boolean;
301case TypeCode.String:
302case TypeCode.Char:
303case TypeCode.DateTime:
304return JsonType.@string;
305case TypeCode.Int16:
306case TypeCode.Int32:
307case TypeCode.Int64:
308case TypeCode.UInt16:
309case TypeCode.UInt32:
310case TypeCode.UInt64:
311case TypeCode.Single:
312case TypeCode.Double:
313case TypeCode.Decimal:
314case TypeCode.SByte:
315case TypeCode.Byte:
316return JsonType.number;
317case TypeCode.Object:
318if (obj is DynamicJson)
319return ((DynamicJson)obj).IsArray ? JsonType.array : JsonType.@object;
320else
321return (obj is IEnumerable) ? JsonType.array : JsonType.@object;
322case TypeCode.DBNull:
323case TypeCode.Empty:
324default:
325return JsonType.@null;
326}
327}
328
329private static XAttribute CreateTypeAttr(JsonType type)
330{
331return new XAttribute("type", type.ToString());
332}
333
334private static object CreateJsonNode(object obj)
335{
336var type = GetJsonType(obj);
337switch (type)
338{
339case JsonType.@string:
340case JsonType.number:
341return obj;
342case JsonType.boolean:
343return obj.ToString().ToLower();
344case JsonType.@object:
345return CreateXObject(obj);
346case JsonType.array:
347if (obj is DynamicJson)
348return CreateXObject(obj);
349
350return CreateXArray(obj as IEnumerable);
351case JsonType.@null:
352default:
353return null;
354}
355}
356
357private static IEnumerable<XStreamingElement> CreateXArray<T>(T obj) where T : IEnumerable
358{
359return obj.Cast<object>()
360.Select(o => new XStreamingElement("item", CreateTypeAttr(GetJsonType(o)), CreateJsonNode(o)));
361}
362
363private static IEnumerable<XStreamingElement> CreateXObject(object obj)
364{
365DynamicJson dj = obj as DynamicJson;
366if (dj != null)
367{
368if (dj.IsArray)
369{
370var ret = new List<XStreamingElement>();
371
372foreach(var item in (dynamic)dj)
373ret.Add(new XStreamingElement("item", CreateTypeAttr(GetJsonType(item)), CreateJsonNode(item)));
374
375return ret.ToArray();
376}
377else
378{
379return 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
385return 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
391private static string CreateJsonString(XStreamingElement element)
392{
393using (var ms = new MemoryStream())
394{
395var methods = typeof(JsonReaderWriterFactory).GetMethods(BindingFlags.Static | BindingFlags.Public);
396var createJsonWriterEx = methods.FirstOrDefault(method => method.Name == "CreateJsonWriter" && method.GetParameters().Length == 4);
397
398using (var writer = createJsonWriterEx == null ?
399JsonReaderWriterFactory.CreateJsonWriter(ms, Encoding.UTF8, false) :
400createJsonWriterEx.Invoke(null, new object[] { ms, Encoding.UTF8, false, true }) as System.Xml.XmlDictionaryWriter)
401{
402element.WriteTo(writer);
403writer.Flush();
404return Encoding.UTF8.GetString(ms.ToArray());
405}
406}
407}
408
409// dynamic structure represents JavaScript Object/Array
410
411XElement xml;
412JsonType jsonType;
413
414/// <summary>create blank JSObject</summary>
415public DynamicJson()
416{
417xml = new XElement("root", CreateTypeAttr(JsonType.@object));
418jsonType = JsonType.@object;
419}
420
421private DynamicJson(XElement element, JsonType type)
422{
423Debug.Assert(type == JsonType.array || type == JsonType.@object);
424
425xml = element;
426jsonType = type;
427}
428
429public bool IsObject { get { return jsonType == JsonType.@object; } }
430
431public bool IsArray { get { return jsonType == JsonType.array; } }
432
433/// <summary>has property or not</summary>
434public bool IsDefined(string name)
435{
436return IsObject && (GetElement(name) != null);
437}
438
439/// <summary>has property or not</summary>
440public bool IsDefined(int index)
441{
442return IsArray && (xml.Elements().ElementAtOrDefault(index) != null);
443}
444
445/// <summary>delete property</summary>
446public bool Remove(string name)
447{
448var elem = GetElement(name);
449if (elem != null)
450{
451elem.Remove();
452return true;
453}
454else return false;
455}
456
457/// <summary>delete property</summary>
458public bool Remove(int index)
459{
460var elem = xml.Elements().ElementAtOrDefault(index);
461if (elem != null)
462{
463elem.Remove();
464return true;
465}
466else return false;
467}
468
469/// <summary>mapping to Array or Class by Public PropertyName</summary>
470public T Deserialize<T>()
471{
472return (T)Deserialize(typeof(T));
473}
474
475private object Deserialize(Type type)
476{
477return (IsArray) ? DeserializeArray(type) : DeserializeObject(type);
478}
479
480private dynamic DeserializeValue(XElement element, Type elementType)
481{
482var value = ToValue(element);
483if (value is DynamicJson)
484{
485value = ((DynamicJson)value).Deserialize(elementType);
486}
487return Convert.ChangeType(value, elementType);
488}
489
490private object DeserializeObject(Type targetType)
491{
492var result = Activator.CreateInstance(targetType);
493var dict = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
494.Where(p => p.CanWrite)
495.ToDictionary(pi => pi.Name, pi => pi);
496foreach (var item in xml.Elements())
497{
498PropertyInfo propertyInfo;
499if (!dict.TryGetValue(item.Name.LocalName, out propertyInfo)) continue;
500var value = DeserializeValue(item, propertyInfo.PropertyType);
501propertyInfo.SetValue(result, value, null);
502}
503return result;
504}
505
506private object DeserializeArray(Type targetType)
507{
508if (targetType.IsArray) // Foo[]
509{
510var elemType = targetType.GetElementType();
511dynamic array = Array.CreateInstance(elemType, xml.Elements().Count());
512var index = 0;
513foreach (var item in xml.Elements())
514{
515array[index++] = DeserializeValue(item, elemType);
516}
517return array;
518}
519else // List<Foo>
520{
521var elemType = targetType.GetGenericArguments()[0];
522dynamic list = Activator.CreateInstance(targetType);
523foreach (var item in xml.Elements())
524{
525list.Add(DeserializeValue(item, elemType));
526}
527return list;
528}
529}
530
531// Delete
532public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
533{
534result = (IsArray)
535? Remove((int)args[0])
536: Remove((string)args[0]);
537return true;
538}
539
540// IsDefined, if has args then TryGetMember
541public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
542{
543if (args.Length > 0)
544{
545result = null;
546return false;
547}
548
549result = IsDefined(binder.Name);
550return true;
551}
552
553// Deserialize or foreach(IEnumerable)
554public override bool TryConvert(ConvertBinder binder, out object result)
555{
556if (binder.Type == typeof(IEnumerable) || binder.Type == typeof(object[]))
557{
558var ie = (IsArray)
559? xml.Elements().Select(x => ToValue(x))
560: xml.Elements().Select(x => (dynamic)new KeyValuePair<string, object>(x.Name.LocalName, ToValue(x)));
561result = (binder.Type == typeof(object[])) ? ie.ToArray() : ie;
562}
563else
564{
565result = Deserialize(binder.Type);
566}
567return true;
568}
569
570private bool TryGet(XElement element, out object result)
571{
572if (element == null)
573{
574result = null;
575return false;
576}
577
578result = ToValue(element);
579return true;
580}
581
582public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
583{
584return (IsArray)
585? TryGet(xml.Elements().ElementAtOrDefault((int)indexes[0]), out result)
586: TryGet(GetElement((string)indexes[0]), out result);
587}
588
589public override bool TryGetMember(GetMemberBinder binder, out object result)
590{
591if (IsArray)
592{
593if (binder.Name == "Length")
594{
595result = xml.Elements().Count();
596return true;
597}
598
599return TryGet(xml.Elements().ElementAtOrDefault(int.Parse(binder.Name)), out result);
600}
601
602var element = GetElement(binder.Name);
603if (element == null)
604{
605XNamespace ns = "item";
606XElement tempElement;
607
608if (KeyHasInvalidXmlChars(binder.Name))
609tempElement = new XElement(ns + "item", new XAttribute("item", binder.Name), CreateTypeAttr(JsonType.@object));
610else
611tempElement = new XElement(binder.Name, CreateTypeAttr(JsonType.@object));
612
613result = new DynamicJson(tempElement, JsonType.@object) { _temporaryParentObject = this };
614return true;
615}
616
617return TryGet(element, out result);
618}
619
620private DynamicJson _temporaryParentObject;
621
622private void AssignTemporaryParentObject()
623{
624if (_temporaryParentObject != null)
625{
626_temporaryParentObject.xml.Add(xml);
627
628_temporaryParentObject.AssignTemporaryParentObject();
629_temporaryParentObject = null;
630}
631}
632
633private bool TrySet(string name, object value)
634{
635AssignTemporaryParentObject();
636
637var type = GetJsonType(value);
638var element = GetElement(name);
639if (element == null)
640{
641XNamespace ns = "item";
642
643try
644{
645if (KeyHasInvalidXmlChars(name))
646xml.Add(new XElement(ns + "item", new XAttribute("item", name), CreateTypeAttr(type), CreateJsonNode(value)));
647else
648xml.Add(new XElement(name, CreateTypeAttr(type), CreateJsonNode(value)));
649}
650catch
651{
652xml.Add(new XElement(ns + "item", new XAttribute("item", name), CreateTypeAttr(type), CreateJsonNode(value)));
653}
654}
655else
656{
657element.Attribute("type").Value = type.ToString();
658element.ReplaceNodes(CreateJsonNode(value));
659}
660
661return true;
662}
663
664private bool TrySet(int index, object value)
665{
666AssignTemporaryParentObject();
667
668var type = GetJsonType(value);
669var e = xml.Elements().ElementAtOrDefault(index);
670if (e == null)
671{
672xml.Add(new XElement("item", CreateTypeAttr(type), CreateJsonNode(value)));
673}
674else
675{
676e.Attribute("type").Value = type.ToString();
677e.ReplaceNodes(CreateJsonNode(value));
678}
679
680return true;
681}
682
683public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
684{
685return (IsArray)
686? TrySet((int)indexes[0], value)
687: TrySet((string)indexes[0], value);
688}
689
690public override bool TrySetMember(SetMemberBinder binder, object value)
691{
692return (IsArray)
693? TrySet(int.Parse(binder.Name), value)
694: TrySet(binder.Name, value);
695}
696
697public override IEnumerable<string> GetDynamicMemberNames()
698{
699return (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>
705public override string ToString()
706{
707// <foo type="null"></foo> is can't serialize. replace to <foo type="null" />
708foreach (var elem in xml.Descendants().Where(x => x.Attribute("type").Value == "null"))
709{
710elem.RemoveNodes();
711}
712return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(jsonType), xml.Elements()));
713}
714
715enum JsonType
716{
717@string, number, boolean, @object, array, @null
718}
719}
720
721
722}