diff --git a/Sources/LightJson/.editorconfig b/Sources/LightJson/.editorconfig new file mode 100644 index 0000000..88315cb --- /dev/null +++ b/Sources/LightJson/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] +indent_style = tab +indent_size = tab +tab_size = 4 \ No newline at end of file diff --git a/Sources/LightJson/JsonObject.cs b/Sources/LightJson/JsonObject.cs index 2f71903..6ff8755 100644 --- a/Sources/LightJson/JsonObject.cs +++ b/Sources/LightJson/JsonObject.cs @@ -56,7 +56,15 @@ public JsonValue this[string key] /// public JsonObject() { - this.properties = new Dictionary(); + if (JsonOptions.PropertyNameCaseInsensitive) + { + var comparer = StringComparer.OrdinalIgnoreCase; + this.properties = new Dictionary(comparer); + } + else + { + this.properties = new Dictionary(); + } } /// diff --git a/Sources/LightJson/JsonOptions.cs b/Sources/LightJson/JsonOptions.cs new file mode 100644 index 0000000..99bb06d --- /dev/null +++ b/Sources/LightJson/JsonOptions.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace LightJson; + +public static class JsonOptions +{ + /// + /// Gets or sets a value that indicates whether a property's name uses a case-insensitive comparison when getting + /// values. + /// + public static bool PropertyNameCaseInsensitive { get; set; } = false; + + /// + /// Gets or sets whether the JSON serializer should throw an exception when trying to convert a value to an invalid type. + /// + public static bool ThrowOnInvalidCast { get; set; } = false; +} diff --git a/Sources/LightJson/JsonValue.cs b/Sources/LightJson/JsonValue.cs index c5ab194..9f59637 100644 --- a/Sources/LightJson/JsonValue.cs +++ b/Sources/LightJson/JsonValue.cs @@ -2,6 +2,10 @@ using System.Diagnostics; using System.Collections.Generic; using LightJson.Serialization; +using System.Reflection; +using System.Collections; + +#nullable enable namespace LightJson { @@ -13,7 +17,7 @@ namespace LightJson public struct JsonValue { private readonly JsonValueType type; - private readonly object reference; + private readonly object reference = null!; private readonly double value; /// @@ -123,7 +127,7 @@ public bool IsDateTime { get { - return this.AsDateTime is not null; + return this.AsDateTime != default; } } @@ -139,18 +143,22 @@ public bool AsBoolean case JsonValueType.Boolean: return (this.value == 1); - case JsonValueType.Number: - return (this.value != 0); + // this shoulnd't implicit check if value is defined in order to return an + // boolean value. instead, it should just check if the value is an boolean, + // in order to return an boolean. - case JsonValueType.String: - return ((string)this.reference != ""); + //case JsonValueType.Number: + // return (this.value != 0); - case JsonValueType.Object: - case JsonValueType.Array: - return true; + //case JsonValueType.String: + // return ((string)this.reference != ""); + + //case JsonValueType.Object: + //case JsonValueType.Array: + // return true; default: - return false; + return ThrowOrNull(); } } } @@ -207,7 +215,7 @@ public double AsNumber } default: - return 0; + return ThrowOrNull(); } } } @@ -215,7 +223,7 @@ public double AsNumber /// /// Gets this value as a String type. /// - public string AsString + public string? AsString { get { @@ -233,7 +241,7 @@ public string AsString return (string)this.reference; default: - return null; + return ThrowOrNull(); } } } @@ -241,33 +249,33 @@ public string AsString /// /// Gets this value as an JsonObject. /// - public JsonObject AsJsonObject + public JsonObject? AsJsonObject { get { return (this.IsJsonObject) ? (JsonObject)this.reference - : null; + : ThrowOrNull(); } } /// /// Gets this value as an JsonArray. /// - public JsonArray AsJsonArray + public JsonArray? AsJsonArray { get { return (this.IsJsonArray) ? (JsonArray)this.reference - : null; + : ThrowOrNull(); } } /// /// Gets this value as a system.DateTime. /// - public DateTime? AsDateTime + public DateTime AsDateTime // it would never return an null datetime, even if theres no value on it. { get { @@ -277,7 +285,7 @@ public DateTime? AsDateTime } else { - return null; + return ThrowOrNull(); } } } @@ -285,7 +293,7 @@ public DateTime? AsDateTime /// /// Gets this (inner) value as a System.object. /// - public object AsObject + public object? AsObject { get { @@ -372,6 +380,13 @@ public JsonValue this[int index] } } + private T? ThrowOrNull() + { + return JsonOptions.ThrowOnInvalidCast ? + throw new InvalidCastException($"Cannot cast an value of type {Type} into {typeof(T).Name}.") + : default; + } + /// /// Initializes a new instance of the JsonValue struct. /// @@ -384,11 +399,88 @@ public JsonValue this[int index] /// The internal value reference of the JsonValue. /// This value is used when the Json type is String, JsonObject, or JsonArray. /// - private JsonValue(JsonValueType type, double value, object reference) + private JsonValue(JsonValueType type, double value, object? reference) { - this.type = type; - this.value = value; - this.reference = reference; + this.type = type; + this.value = value; + this.reference = reference!; + } + + private static JsonValue DetermineSingle(object? value, int deepness, out JsonValueType valueType) + { + if (deepness > 128) + { + throw new InvalidOperationException("The maximum JSON depth level has been reached."); + } + + if (value is null) + { + valueType = JsonValueType.Null; + return new JsonValue(valueType, 0, null); + } + else if (value is string) + { + valueType = JsonValueType.String; + return new JsonValue(valueType, 0, value); + } + else if (value is int nint) + { + valueType = JsonValueType.Number; + return new JsonValue(valueType, nint, null); + } + else if (value is double ndbl) + { + valueType = JsonValueType.Number; + return new JsonValue(valueType, ndbl, null); + } + else if (value is bool nbool) + { + valueType = JsonValueType.Boolean; + return new JsonValue(valueType, nbool ? 1 : 0, null); + } + else if (value.GetType().IsArray) + { + JsonArray arr = new JsonArray(); + foreach (object? item in (IEnumerable)value) + { + if (item == null) continue; + arr.Add(DetermineSingle(item, deepness + 1, out _)); + } + + valueType = JsonValueType.Array; + return new JsonValue(valueType, 0, arr); + } + else + { + JsonObject newObj = new JsonObject(); + PropertyInfo[] valueProperties = value.GetType() + .GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (PropertyInfo property in valueProperties) + { + string name = property.Name; + object? v = property.GetValue(value); + + JsonValue jsonValue = DetermineSingle(v, deepness + 1, out _); + newObj.Add(name, jsonValue); + } + + valueType = JsonValueType.Object; + return new JsonValue(JsonValueType.Object, 0, newObj); + } + } + + /// + /// Initializes a new instance of the JsonValue struct, representing a dynamic value. + /// + /// The value to be wrapped. + public JsonValue(object value) + { + JsonValue _value = DetermineSingle(value, 0, out JsonValueType valueType); + + this.type = valueType; + this.reference = _value.reference; + this.value = _value.value; } /// @@ -399,7 +491,7 @@ public JsonValue(bool? value) { if (value.HasValue) { - this.reference = null; + this.reference = null!; this.type = JsonValueType.Boolean; @@ -419,7 +511,7 @@ public JsonValue(double? value) { if (value.HasValue) { - this.reference = null; + this.reference = null!; this.type = JsonValueType.Number; @@ -566,7 +658,14 @@ public static implicit operator int(JsonValue jsonValue) } else { - return 0; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to Int32."); + } + else + { + return 0; + } } } @@ -602,7 +701,14 @@ public static implicit operator bool(JsonValue jsonValue) } else { - return false; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to Boolean."); + } + else + { + return false; + } } } @@ -638,7 +744,14 @@ public static implicit operator double(JsonValue jsonValue) } else { - return double.NaN; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to Double."); + } + else + { + return Double.NaN; + } } } @@ -666,15 +779,22 @@ public static implicit operator double(JsonValue jsonValue) /// Converts the given JsonValue into a String. /// /// The JsonValue to be converted. - public static implicit operator string(JsonValue jsonValue) + public static implicit operator string?(JsonValue jsonValue) { if (jsonValue.IsString || jsonValue.IsNull) { - return jsonValue.reference as string; + return jsonValue.AsString; } else { - return null; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to String."); + } + else + { + return null; + } } } @@ -682,7 +802,7 @@ public static implicit operator string(JsonValue jsonValue) /// Converts the given JsonValue into a JsonObject. /// /// The JsonValue to be converted. - public static implicit operator JsonObject(JsonValue jsonValue) + public static implicit operator JsonObject?(JsonValue jsonValue) { if (jsonValue.IsJsonObject || jsonValue.IsNull) { @@ -690,7 +810,14 @@ public static implicit operator JsonObject(JsonValue jsonValue) } else { - return null; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to JsonObject."); + } + else + { + return null; + } } } @@ -698,7 +825,7 @@ public static implicit operator JsonObject(JsonValue jsonValue) /// Converts the given JsonValue into a JsonArray. /// /// The JsonValue to be converted. - public static implicit operator JsonArray(JsonValue jsonValue) + public static implicit operator JsonArray?(JsonValue jsonValue) { if (jsonValue.IsJsonArray || jsonValue.IsNull) { @@ -706,7 +833,14 @@ public static implicit operator JsonArray(JsonValue jsonValue) } else { - return null; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to JsonArray."); + } + else + { + return null; + } } } @@ -716,15 +850,20 @@ public static implicit operator JsonArray(JsonValue jsonValue) /// The JsonValue to be converted. public static implicit operator DateTime(JsonValue jsonValue) { - var dateTime = jsonValue.AsDateTime; - - if (dateTime.HasValue) + if (jsonValue.IsDateTime) { - return dateTime.Value; + return jsonValue.AsDateTime; } else { - return DateTime.MinValue; + if (JsonOptions.ThrowOnInvalidCast) + { + throw new InvalidCastException($"Cannot cast value of type {jsonValue.Type} to DateTime."); + } + else + { + return default; + } } } @@ -779,7 +918,7 @@ public static JsonValue Parse(string text) /// Returns a value indicating whether this JsonValue is equal to the given object. /// /// The object to test. - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is null) { @@ -847,7 +986,7 @@ private class JsonValueDebugView private JsonValue jsonValue; [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public JsonObject ObjectView + public JsonObject? ObjectView { get { @@ -863,7 +1002,7 @@ public JsonObject ObjectView } [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public JsonArray ArrayView + public JsonArray? ArrayView { get { @@ -911,4 +1050,4 @@ public JsonValueDebugView(JsonValue jsonValue) } } } -} +} \ No newline at end of file diff --git a/Sources/LightJson/LightJson - Backup.csproj b/Sources/LightJson/LightJson - Backup.csproj new file mode 100644 index 0000000..00ade9e --- /dev/null +++ b/Sources/LightJson/LightJson - Backup.csproj @@ -0,0 +1,62 @@ + + + + + netcoreapp3.1; + netstandard1.6; + netstandard2.0; + netstandard2.1; + net35; + net452; + net462; + net472; + net48; + net6.0 + + + true + True + + latest + 0.5.3 + Marcos López C. + A minimalist JSON library designed to easily encode and decode JSON messages. + Copyright © 2023 Marcos López C. + MIT + MarcosLopezC.LightJson + https://github.com/MarcosLopezC/LightJson.git + true + JSON + git + README.md + + true + true + snupkg + + + + true + + + + + + + + + true + build\ + true + + + + true + src\LightJson\%(RecursiveDir)%(Filename).g%(Extension) + true + + + + + + diff --git a/Sources/LightJson/LightJson.csproj b/Sources/LightJson/LightJson.csproj index 00ade9e..105f090 100644 --- a/Sources/LightJson/LightJson.csproj +++ b/Sources/LightJson/LightJson.csproj @@ -41,7 +41,7 @@ - + @@ -55,8 +55,6 @@ src\LightJson\%(RecursiveDir)%(Filename).g%(Extension) true - -