From b65cfd952e1264aa97dc6e46cb258f89770b9ce9 Mon Sep 17 00:00:00 2001 From: Harald Wolff Date: Thu, 23 Nov 2017 13:05:58 +0100 Subject: [PATCH] WIP --- FileBackedJSONValue.cs | 45 +++ JSON.cs | 232 ++++++---------- JSONArray.cs | 10 + JSONConverter.cs | 350 ++++++++++++++++++++++++ JSONObject.cs | 2 +- JSONParser.cs | 3 + JSONWebRequest.cs | 12 +- attributes/JSONClassPolicy.cs | 10 + JSONField.cs => attributes/JSONField.cs | 2 +- json.test/Program.cs | 24 +- network/JSONTcpClient.cs | 51 ++++ network/JSONTcpServer.cs | 74 +++++ sharp.json.csproj | 11 +- 13 files changed, 660 insertions(+), 166 deletions(-) create mode 100644 FileBackedJSONValue.cs create mode 100644 JSONConverter.cs create mode 100644 attributes/JSONClassPolicy.cs rename JSONField.cs => attributes/JSONField.cs (75%) create mode 100644 network/JSONTcpClient.cs create mode 100644 network/JSONTcpServer.cs diff --git a/FileBackedJSONValue.cs b/FileBackedJSONValue.cs new file mode 100644 index 0000000..833019d --- /dev/null +++ b/FileBackedJSONValue.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +namespace sharp.json +{ + public class FileBackedJSONValue + { + public string FileName { get; set; } + public DateTime LoadedWriteTime { get; set; } + + T current; + JSON jsource; + + public FileBackedJSONValue(string filename) + { + FileName = filename; + } + + public T CurrentValue { + get + { + if (!File.Exists(FileName)){ + current = default(T); + } else if ((current == null) || (File.GetLastWriteTime(FileName) != LoadedWriteTime)) + { + jsource = JSON.ReadFrom(FileName); + LoadedWriteTime = File.GetLastWriteTime(FileName); + + current = jsource.To(); + } + return current; + } + set + { + current = value; + Save(); + } + } + + public void Save(){ + JSONConverter.From(current).WriteTo(FileName,true); + LoadedWriteTime = File.GetLastWriteTime(FileName); + } + + } +} diff --git a/JSON.cs b/JSON.cs index 8cdcbbb..8faa75c 100644 --- a/JSON.cs +++ b/JSON.cs @@ -6,6 +6,8 @@ using System.Net; using System.Reflection; using System.Collections; using System.Security.Cryptography; +using System.CodeDom; +using System.Globalization; namespace sharp.json { @@ -17,6 +19,7 @@ namespace sharp.json public virtual long Integer { get { throw new NotImplementedException(); } } public virtual string String { get { throw new NotImplementedException(); } } public virtual bool Boolean { get { throw new NotImplementedException(); } } + public virtual object Object { get { throw new NotImplementedException(); } } public virtual JSON this[int n] { @@ -78,142 +81,108 @@ namespace sharp.json return b ? JSONSpecial.True : JSONSpecial.False; } - public T To(){ - object o = Create(typeof(T)); - return (T)o; + public static implicit operator string(JSON json) + { + return json.String; + } + public static implicit operator bool(JSON json) + { + return json.Boolean; + } + public static implicit operator int(JSON json) + { + return (int)json.Integer; + } + public static implicit operator long(JSON json) + { + return json.Integer; + } + public static implicit operator double(JSON json) + { + return json.Double; } - public void WriteTo(Stream stream){ - byte[] data = Encoding.ASCII.GetBytes(ToString()); + public T To() + { + return JSONConverter.To(this); + } + + public static JSON From(object o){ + return JSONConverter.From(o); + } + + + public void WriteTo(Stream stream) + { + WriteTo(stream); + } + public void WriteTo(Stream stream,bool pretty){ + byte[] data = Encoding.ASCII.GetBytes(pretty ? prettyFormat() : ToString() ); stream.Write(data,0,data.Length); } public void WriteTo(String filename) + { + WriteTo(filename, false); + } + public void WriteTo(String filename,bool pretty) { FileStream fs = new FileStream(filename, FileMode.Create); - WriteTo(fs); + WriteTo(fs,pretty); fs.Close(); } - public static JSON ReadFrom(string filename){ + public static JSON ReadFrom(string filename) + { + return ReadFrom(filename, null); + } + + public static JSON ReadFrom(string filename, object defaultValue) + { JSONParser parser = new JSONParser(); - string source = File.ReadAllText(filename); + string source = ""; + try + { + source = File.ReadAllText(filename); + } + catch (FileNotFoundException e) + { + return JSON.From(defaultValue); + } return parser.Parse(source); - } - private object Create(Type t) - { - if (t.IsPrimitive || (t == typeof(string)) ) + public static JSON ReadFrom(string filename,JSON defaultValue){ + JSONParser parser = new JSONParser(); + string source = ""; + try { - return CreatePrimitive(t); + source = File.ReadAllText(filename); + } catch (FileNotFoundException e){ + return defaultValue; } - - if (this.JSONType == JSONTypes.Object) - { - return CreateObject(t); - } - - if ((this.JSONType == JSONTypes.Array) && (t.IsArray)) - { - return CreateArray(t); - } - - throw new InvalidCastException(String.Format("JSON {0} can't be casted to {1}", this.JSONType.ToString(), t.Name)); + return parser.Parse(source); } - private object CreatePrimitive(Type t){ - - if ((this.JSONType == JSONTypes.Null)){ - return null; - } - - if ((this.JSONType == JSONTypes.Array) || (this.JSONType == JSONTypes.Object)){ - throw new InvalidCastException(String.Format("JSON {0} can't be casted to {1}",this.JSONType.ToString(),t.Name)); - } - - if (t.Equals(typeof(int)) || t.Equals(typeof(long)) || t.Equals(typeof(short))){ - return (object)this.Integer; - } - if (t.Equals(typeof(double)) || t.Equals(typeof(float))) + public object Native(){ + switch (JSONType) { - return (object)this.Double; + case JSONTypes.Null: + return null; + case JSONTypes.True: + return true; + case JSONTypes.False: + return false; + case JSONTypes.Number: + return Double; + case JSONTypes.String: + return String; } - if (t.Equals(typeof(string))) - { - return (object)this.String; - } - if (t.Equals(typeof(Boolean))) - { - return (object)this.Boolean; - } - - throw new Exception(String.Format("Unsupported primitive type",t.Name)); + throw new InvalidCastException(String.Format("Can't create native type of {0}",this.JSONType)); } - private object CreateObject(Type t){ - if (t.GetConstructor(new Type[] { typeof(JSON) } ) != null){ - return Activator.CreateInstance(t, this); - } - object instance = Activator.CreateInstance(t); - Dictionary assignedFields = new Dictionary(); - - foreach (FieldInfo fi in t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) - { - foreach (JSONField f in fi.GetCustomAttributes()) - { - if (this.Contains(f.Alias)) - { - if (!assignedFields.ContainsKey(f.Alias) || assignedFields[f.Alias].IsAutoAssigned){ - assignedFields[f.Alias] = new FieldPropertyInfo(fi, false); - break; - } - } - } - if (!assignedFields.ContainsKey(fi.Name) && this.Contains(fi.Name)){ - assignedFields[fi.Name] = new FieldPropertyInfo(fi, true); - } - } - - foreach (PropertyInfo pi in t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) - { - foreach (JSONField f in pi.GetCustomAttributes()) - { - if (this.Contains(f.Alias)) - { - if (!assignedFields.ContainsKey(f.Alias) || assignedFields[f.Alias].IsAutoAssigned) - { - assignedFields[f.Alias] = new FieldPropertyInfo(pi, false); - break; - } - } - } - if (!assignedFields.ContainsKey(pi.Name) && this.Contains(pi.Name)) - { - assignedFields[pi.Name] = new FieldPropertyInfo(pi, true); - } - } - - foreach (KeyValuePair e in assignedFields){ - e.Value.setValue(instance, this[e.Key].Create(e.Value.ValueType)); - } - - return instance; - } - - private object CreateArray(Type t) - { - Array a = Array.CreateInstance(t.GetElementType(), this.Count); - - for (int n = 0; n < this.Count;n++){ - a.SetValue(this[n].Create(t.GetElementType()),n); - } - - return a; - } - public virtual IEnumerator GetEnumerator() { throw new NotImplementedException(); @@ -223,46 +192,5 @@ namespace sharp.json { return (IEnumerator)GetEnumerator(); } - - - struct FieldPropertyInfo { - public bool IsAutoAssigned { get; set; } - FieldInfo FieldInfo { get; set; } - PropertyInfo PropertyInfo { get; set; } - - public FieldPropertyInfo(PropertyInfo propertyInfo,bool autoAssigned) - { - this.IsAutoAssigned = autoAssigned; - this.PropertyInfo = propertyInfo; - this.FieldInfo = null; - } - public FieldPropertyInfo(FieldInfo fieldInfo,bool autoAssigned) - { - this.IsAutoAssigned = autoAssigned; - this.PropertyInfo = null; - this.FieldInfo = fieldInfo; - } - - public void setValue(object inst,object value){ - if (PropertyInfo != null){ - PropertyInfo.SetValue(inst,value); - } else if (FieldInfo != null){ - FieldInfo.SetValue(inst,value); - } - } - - public Type ValueType { - get { - if (PropertyInfo != null){ - return PropertyInfo.PropertyType; - } else if (FieldInfo != null){ - return FieldInfo.FieldType; - } - return null; - } - } - - } - } } diff --git a/JSONArray.cs b/JSONArray.cs index 4678449..fcf8709 100644 --- a/JSONArray.cs +++ b/JSONArray.cs @@ -9,6 +9,16 @@ namespace sharp.json public JSONArray() : base(JSONTypes.Array) { } + public JSONArray(object[] init) + : base(JSONTypes.Array) + { + foreach (object o in init) + { + this.values.Add(JSONConverter.From(o)); + } + + } + List values = new List(); public override bool Boolean diff --git a/JSONConverter.cs b/JSONConverter.cs new file mode 100644 index 0000000..81628c3 --- /dev/null +++ b/JSONConverter.cs @@ -0,0 +1,350 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using sharp.json.attributes; +using System.Reflection; +using sharp.extensions; +namespace sharp.json +{ + public static class JSONConverter + { + delegate object ConvertToDelegate(Type t,JSON json); + + static Dictionary toDelegates = new Dictionary{ + { typeof(string), (t, json) => json.String }, + { typeof(int), (t, json) => json.Integer }, + { typeof(short), (t, json) => json.Integer }, + { typeof(long), (t, json) => json.Integer }, + { typeof(uint), (t, json) => json.Integer }, + { typeof(ushort), (t, json) => json.Integer }, + { typeof(ulong), (t, json) => json.Integer }, + { typeof(double), (t, json) => json.Double }, + { typeof(float), (t, json) => json.Double }, + { typeof(bool), (t, json) => json.Boolean }, + { typeof(DateTime), (t, json) => DateTime.Parse(json.String, CultureInfo.InvariantCulture) }, + { typeof(Guid), (t, json) => Guid.Parse(json.String)}, + { typeof(object), ToObject } + }; + + public static T To(JSON json) + { + Type t = typeof(T); + return (T)(object)To(t, json); + } + + public static JSON From(object value) + { + if (value == null) + { + return JSONSpecial.Null; + } + return From(value.GetType(), value); + } + + public static object To(Type t,JSON json){ + + if (json.JSONType == JSONTypes.Null){ + return null; + } + + if (t.IsEnum){ + return Enum.Parse(t, json.String); + } + + if (toDelegates.ContainsKey(t)) + { + return toDelegates[t](t, json); + } + + if (t.IsArray){ + return ToArray(t, json); + } + + if (!t.IsPrimitive){ + return ToObject(t, json); + } + + + throw new InvalidCastException(String.Format("JSON {0} can't be casted to {1}", json.JSONType.ToString(), t.Name)); + + } + + public static void ApplyObject(JSON json, object o) + { + FieldPropertyInfo[] fpilist = fpi(o.GetType()); + foreach (FieldPropertyInfo fpi in fpilist) + { + if (json.Contains(fpi.Key)) + { + fpi.setValue(o, To(fpi.ValueType, json[fpi.Key])); + } + } + + } + + public static object ToObject(Type t,JSON json){ + ConstructorInfo ci = t.GetConstructor(new Type[] { typeof(JSON) }); + if (ci != null) + { + if (ci.GetParameters()[0].ParameterType.Equals(typeof(JSON))) + { + return Activator.CreateInstance(t, (object)json); + } + } + + JSONClassPolicy cp = t.GetCustomAttribute(); + if (cp == null) + { + cp = new JSONClassPolicy(); + } + return ToObject(t, json, cp.Policy); + } + + + private static object ToObject(Type t, JSON json,JSONPolicy policy) + { + object oi = Activator.CreateInstance(t); + + ApplyObject(json,oi); + + return oi; + } + + private static object ToArray(Type t,JSON json) + { + Array a = Array.CreateInstance(t.GetElementType(), json.Count); + + for (int n = 0; n < json.Count; n++) + { + a.SetValue(To(t.GetElementType(),json[n]), n); + } + + return a; + } + + public static JSON From(Type t, object value) + { + if (value == null) + { + return JSONSpecial.Null; + } + + if (t == null) + { + t = value.GetType(); + } + + if ((t.IsPrimitive) || (t == typeof(string))) + { + return FromPrimitive(value); + } + + if (t == typeof(DateTime)) + { + return new JSONString(((DateTime)value).ToString(CultureInfo.InvariantCulture)); + } + + if (t == typeof(Guid)) + { + return ((Guid)value).ToString(); + } + + if (t.IsEnum) + { + return new JSONString(value.ToString()); + } + + if (t.IsArray) + { + JSONArray ja = new JSONArray(); + + Array a = (Array)value; + + Type et = t.GetElementType(); + if (et == typeof(object)) + { + et = null; + } + foreach (object e in a) + { + ja.Add(From(et, e)); + } + + return ja; + } + + return FromObject(value); + } + + public static JSON FromPrimitive(object value) + { + Type t = value.GetType(); + + if (typeof(int).Equals(t)) + { + return new JSONNumber((int)value); + } + if (typeof(long).Equals(t)) + { + return new JSONNumber((long)value); + } + if (typeof(double).Equals(t)) + { + return new JSONNumber((double)value); + } + if (typeof(float).Equals(t)) + { + return new JSONNumber((float)value); + } + if (typeof(string).Equals(t)) + { + return new JSONString(value.ToString()); + } + if (typeof(bool).Equals(t)) + { + return ((bool)value) ? JSONSpecial.True : JSONSpecial.False; + } + + throw new NotImplementedException(String.Format("Cast of {0} to JSON is not (yet) supported", t.Name)); + } + + public static JSON FromObject(object value) + { + return FromObject(value.GetType(), value); + } + public static JSON FromObject(Type type, object value) + { + Type t = value.GetType(); + JSONObject jo = new JSONObject(); + + foreach (FieldPropertyInfo fpi in fpi(t)){ + jo[fpi.Key] = JSON.From(fpi.getValue(value)); + } + + return jo; + } + + private static FieldPropertyInfo[] fpi(Type t) + { + JSONClassPolicy cp = t.GetCustomAttribute(); + if (cp == null) + { + cp = new JSONClassPolicy(); + } + return fpi(t, cp); + } + + private static FieldPropertyInfo[] fpi(Type t,JSONClassPolicy cp) + { + BindingFlags bf; + switch (cp.Policy){ + case JSONPolicy.ALL: + bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + break; + case JSONPolicy.ATTRIBUTED: + bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + break; + case JSONPolicy.NONPUBLIC: + bf = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; + break; + default: + bf = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; + break; + } + + + List fpilist = new List(); + + foreach (FieldInfo fi in t.GetFields(bf)) + { + fpilist.Add(new FieldPropertyInfo(fi)); + } + foreach (PropertyInfo pi in t.GetProperties(bf)) + { + fpilist.Add(new FieldPropertyInfo(pi)); + } + + FieldPropertyInfo[] temp = fpilist.ToArray(); + fpilist.Clear(); + + foreach (FieldPropertyInfo fpi in temp){ + if ((cp.Policy != JSONPolicy.ATTRIBUTED) || (fpi.JSONField != null)) + { + fpilist.Add(fpi); + } + } + + if (!t.BaseType.IsNull()){ + fpilist.AddRange(fpi(t.BaseType,cp)); + } + + return fpilist.ToArray(); + } + + struct FieldPropertyInfo + { + public String Key { get; set; } + + FieldInfo FieldInfo { get; set; } + PropertyInfo PropertyInfo { get; set; } + + public JSONField JSONField { get; set; } + + public FieldPropertyInfo(PropertyInfo propertyInfo) + { + this.PropertyInfo = propertyInfo; + this.FieldInfo = null; + this.JSONField = propertyInfo.GetCustomAttribute(); + this.Key = (this.JSONField == null) ? propertyInfo.Name : ((this.JSONField.Alias == null) ? propertyInfo.Name : this.JSONField.Alias); + } + public FieldPropertyInfo(FieldInfo fieldInfo) + { + this.PropertyInfo = null; + this.FieldInfo = fieldInfo; + this.JSONField = fieldInfo.GetCustomAttribute(); + this.Key = (this.JSONField == null) ? fieldInfo.Name : ((this.JSONField.Alias == null) ? fieldInfo.Name : this.JSONField.Alias); + } + + public void setValue(object inst, object value) + { + if (PropertyInfo != null) + { + PropertyInfo.SetValue(inst, value); + } + else if (FieldInfo != null) + { + FieldInfo.SetValue(inst, value); + } + } + public object getValue(object inst) + { + if ((PropertyInfo != null)&&(!PropertyInfo.GetMethod.IsNull())) + { + return PropertyInfo.GetValue(inst); + } + else if (FieldInfo != null) + { + return FieldInfo.GetValue(inst); + } + return null; + } + + public Type ValueType + { + get + { + if (PropertyInfo != null) + { + return PropertyInfo.PropertyType; + } + else if (FieldInfo != null) + { + return FieldInfo.FieldType; + } + return null; + } + } + } + + } +} diff --git a/JSONObject.cs b/JSONObject.cs index 09f5189..024e657 100644 --- a/JSONObject.cs +++ b/JSONObject.cs @@ -71,7 +71,7 @@ namespace sharp.json for (int n=0;n-------------------------------------------"); + Console.WriteLine(response.ContentText); + Console.WriteLine("<-------------------------------------------"); + throw e; + } } } diff --git a/attributes/JSONClassPolicy.cs b/attributes/JSONClassPolicy.cs new file mode 100644 index 0000000..5c70973 --- /dev/null +++ b/attributes/JSONClassPolicy.cs @@ -0,0 +1,10 @@ +using System; +namespace sharp.json.attributes +{ + public enum JSONPolicy { ALL, PUBLIC, NONPUBLIC, ATTRIBUTED } + + public class JSONClassPolicy : Attribute + { + public JSONPolicy Policy { get; set; } = JSONPolicy.PUBLIC; + } +} diff --git a/JSONField.cs b/attributes/JSONField.cs similarity index 75% rename from JSONField.cs rename to attributes/JSONField.cs index e85846e..cf54e84 100644 --- a/JSONField.cs +++ b/attributes/JSONField.cs @@ -1,6 +1,6 @@ using System; -namespace sharp.json +namespace sharp.json.attributes { public class JSONField : Attribute { diff --git a/json.test/Program.cs b/json.test/Program.cs index 6ffec39..e7b7f2d 100644 --- a/json.test/Program.cs +++ b/json.test/Program.cs @@ -17,21 +17,25 @@ namespace json.test public static void Main(string[] args) { + JSON json; JSONParser jsonparser = new JSONParser(); - Console.WriteLine("JSON test Patterns:"); - Console.WriteLine(); + json = jsonparser.Parse("4.9125E-05"); + Console.WriteLine(json.ToString()); - foreach (string src in sources){ - json = jsonparser.Parse(src); - Console.WriteLine("NEW PARSER: {0}",json.ToString()); - } + //Console.WriteLine("JSON test Patterns:"); + //Console.WriteLine(); - json = jsonparser.Parse(File.ReadAllText("test.json")); - Console.WriteLine(""); - Console.WriteLine("test.json file:"); - Console.WriteLine("PARSED: {0}",json.ToString()); + //foreach (string src in sources){ + // json = jsonparser.Parse(src); + // Console.WriteLine("NEW PARSER: {0}",json.ToString()); + //} + + //json = jsonparser.Parse(File.ReadAllText("test.json")); + //Console.WriteLine(""); + //Console.WriteLine("test.json file:"); + //Console.WriteLine("PARSED: {0}",json.ToString()); } diff --git a/network/JSONTcpClient.cs b/network/JSONTcpClient.cs new file mode 100644 index 0000000..c252d84 --- /dev/null +++ b/network/JSONTcpClient.cs @@ -0,0 +1,51 @@ +using System; +using System.Net.Sockets; +using System.Net; +using System.IO; + +namespace sharp.json.network +{ + public class JSONTcpClient + { + TcpClient client; + StreamReader reader; + StreamWriter writer; + + JSONParser parser = new JSONParser(); + + public JSONTcpClient(int port) + { + this.client = new TcpClient(); + this.client.Connect("127.0.0.1",port); + this.initialize(); + } + + public JSONTcpClient(TcpClient client) + { + this.client = client; + initialize(); + } + + private void initialize(){ + this.reader = new StreamReader(client.GetStream()); + this.writer = new StreamWriter(client.GetStream()); + } + + + public JSON Receive() + { + string line = reader.ReadLine(); + JSON json = parser.Parse(line); + return json; + } + + public void Send(JSON json){ + writer.WriteLine(json.ToString()); + writer.Flush(); + } + + public void Close(){ + this.client.Close(); + } + } +} diff --git a/network/JSONTcpServer.cs b/network/JSONTcpServer.cs new file mode 100644 index 0000000..416926a --- /dev/null +++ b/network/JSONTcpServer.cs @@ -0,0 +1,74 @@ +using System; +using System.Net.Sockets; +using System.Net; +using System.Threading; + +namespace sharp.json.network +{ + public delegate void JSONClientConnected(JSONTcpClient jsonClient); + + public class JSONTcpServer + { + public JSONClientConnected ClientConnected { get; set; } + + TcpListener listener; + Thread serverThread; + bool shouldExit; + + public JSONTcpServer(int port) + { + this.listener = new TcpListener(IPAddress.Loopback, port); + } + + public void Start() + { + lock (this){ + if (serverThread == null){ + serverThread = new Thread(Serve); + serverThread.Start(); + } + } + } + public void Stop() + { + lock (this){ + shouldExit = true; + this.listener.Stop(); + Monitor.Wait(this); + this.serverThread = null; + } + } + + public void Serve() + { + this.listener.Start(); + + while (true){ + lock (this){ + if (shouldExit){ + shouldExit = false; + Monitor.Pulse(this); + break; + } + } + + try { + TcpClient client = this.listener.AcceptTcpClient(); + ThreadPool.QueueUserWorkItem((state) => fireClientConnected(client)); + } catch (SocketException se){ + } + } + } + + private void fireClientConnected(TcpClient client){ + if (ClientConnected != null){ + JSONTcpClient jsonCLient = new JSONTcpClient(client); + ClientConnected.Invoke(jsonCLient); + } + } + + + + + } +} diff --git a/sharp.json.csproj b/sharp.json.csproj index 77190fe..262fae1 100644 --- a/sharp.json.csproj +++ b/sharp.json.csproj @@ -42,7 +42,12 @@ - + + + + + + @@ -62,6 +67,10 @@ sharp.contracts + + + +