about summary refs log tree commit diff
path: root/Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs
diff options
context:
space:
mode:
authorEmma [it/its]@Rory& <root@rory.gay>2024-05-30 21:39:12 +0200
committerEmma [it/its]@Rory& <root@rory.gay>2024-05-30 21:39:12 +0200
commit4bdea63982dae9c17b7a5fbda38d505655b8d4b3 (patch)
tree8ca9c6bad5f9526c5b36d707f08406fc3bbe2848 /Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs
parentLog warning if registering a duplicate type (diff)
downloadLibMatrix-4bdea63982dae9c17b7a5fbda38d505655b8d4b3.tar.xz
Diffstat (limited to 'Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs')
-rw-r--r--Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs147
1 files changed, 147 insertions, 0 deletions
diff --git a/Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs b/Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs
new file mode 100644
index 0000000..c4ed743
--- /dev/null
+++ b/Utilities/LibMatrix.DebugDataValidationApi/JsonElementExtensions.cs
@@ -0,0 +1,147 @@
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+
+namespace LibMatrix.Extensions;
+
+public static class JsonElementExtensions {
+    public static bool FindExtraJsonElementFields(this JsonElement obj, Type objectType, string objectPropertyName) {
+        if (objectPropertyName == "content" && objectType == typeof(JsonObject))
+            objectType = typeof(StateEventResponse);
+        // if (t == typeof(JsonNode))
+        //     return false;
+
+        Console.WriteLine($"{objectType.Name} {objectPropertyName}");
+        var unknownPropertyFound = false;
+        var mappedPropsDict = objectType.GetProperties()
+            .Where(x => x.GetCustomAttribute<JsonPropertyNameAttribute>() is not null)
+            .ToDictionary(x => x.GetCustomAttribute<JsonPropertyNameAttribute>()!.Name, x => x);
+        objectType.GetProperties().Where(x => !mappedPropsDict.ContainsKey(x.Name))
+            .ToList().ForEach(x => mappedPropsDict.TryAdd(x.Name, x));
+
+        foreach (var field in obj.EnumerateObject()) {
+            if (mappedPropsDict.TryGetValue(field.Name, out var mappedProperty)) {
+                //dictionary
+                if (mappedProperty.PropertyType.IsGenericType &&
+                    mappedProperty.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) {
+                    unknownPropertyFound |= _checkDictionary(field, objectType, mappedProperty.PropertyType);
+                    continue;
+                }
+
+                if (mappedProperty.PropertyType.IsGenericType &&
+                    mappedProperty.PropertyType.GetGenericTypeDefinition() == typeof(List<>)) {
+                    unknownPropertyFound |= _checkList(field, objectType, mappedProperty.PropertyType);
+                    continue;
+                }
+
+                if (field.Name == "content" && (objectType == typeof(StateEventResponse) || objectType == typeof(StateEvent))) {
+                    unknownPropertyFound |= field.FindExtraJsonPropertyFieldsByValueKind(
+                        StateEvent.GetStateEventType(obj.GetProperty("type").GetString()!), // We expect type to always be present
+                        mappedProperty.PropertyType);
+                    continue;
+                }
+
+                unknownPropertyFound |=
+                    field.FindExtraJsonPropertyFieldsByValueKind(objectType, mappedProperty.PropertyType);
+                continue;
+            }
+
+            Console.WriteLine($"[!!] Unknown property {field.Name} in {objectType.Name}!");
+            unknownPropertyFound = true;
+        }
+
+        return unknownPropertyFound;
+    }
+
+    private static bool FindExtraJsonPropertyFieldsByValueKind(this JsonProperty field, Type containerType,
+        Type propertyType) {
+        if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) propertyType = propertyType.GetGenericArguments()[0];
+
+        var switchResult = false;
+        switch (field.Value.ValueKind) {
+            case JsonValueKind.Array:
+                switchResult = field.Value.EnumerateArray().Aggregate(switchResult,
+                    (current, element) => current | element.FindExtraJsonElementFields(propertyType, field.Name));
+                break;
+            case JsonValueKind.Object:
+                switchResult |= field.Value.FindExtraJsonElementFields(propertyType, field.Name);
+                break;
+            case JsonValueKind.True:
+            case JsonValueKind.False:
+                return _checkBool(field, containerType, propertyType);
+            case JsonValueKind.String:
+                return _checkString(field, containerType, propertyType);
+            case JsonValueKind.Number:
+                return _checkNumber(field, containerType, propertyType);
+            case JsonValueKind.Undefined:
+            case JsonValueKind.Null:
+                break;
+            default:
+                throw new ArgumentOutOfRangeException();
+        }
+
+        return switchResult;
+    }
+
+    private static bool _checkBool(this JsonProperty field, Type containerType, Type propertyType) {
+        if (propertyType == typeof(bool)) return true;
+        Console.WriteLine(
+            $"[!!] Encountered bool for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!");
+        return false;
+    }
+
+    private static bool _checkString(this JsonProperty field, Type containerType, Type propertyType) {
+        if (propertyType == typeof(string)) return true;
+        // ReSharper disable once BuiltInTypeReferenceStyle
+        if (propertyType == typeof(String)) return true;
+        Console.WriteLine(
+            $"[!!] Encountered string for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!");
+        return false;
+    }
+
+    private static bool _checkNumber(this JsonProperty field, Type containerType, Type propertyType) {
+        if (propertyType == typeof(int) ||
+            propertyType == typeof(double) ||
+            propertyType == typeof(float) ||
+            propertyType == typeof(decimal) ||
+            propertyType == typeof(long) ||
+            propertyType == typeof(short) ||
+            propertyType == typeof(uint) ||
+            propertyType == typeof(ulong) ||
+            propertyType == typeof(ushort) ||
+            propertyType == typeof(byte) ||
+            propertyType == typeof(sbyte))
+            return true;
+        Console.WriteLine(
+            $"[!!] Encountered number for {field.Name} in {containerType.Name}, the class defines {propertyType.Name}!");
+        return false;
+    }
+
+    private static bool _checkDictionary(this JsonProperty field, Type containerType, Type propertyType) {
+        var keyType = propertyType.GetGenericArguments()[0];
+        var valueType = propertyType.GetGenericArguments()[1];
+        valueType = Nullable.GetUnderlyingType(valueType) ?? valueType;
+        Console.WriteLine(
+            $"Encountered dictionary {field.Name} with key type {keyType.Name} and value type {valueType.Name}!");
+
+        return field.Value.EnumerateObject()
+            .Where(key => !valueType.IsPrimitive && valueType != typeof(string))
+            .Aggregate(false, (current, key) =>
+                current | key.FindExtraJsonPropertyFieldsByValueKind(containerType, valueType)
+            );
+    }
+
+    private static bool _checkList(this JsonProperty field, Type containerType, Type propertyType) {
+        var valueType = propertyType.GetGenericArguments()[0];
+        valueType = Nullable.GetUnderlyingType(valueType) ?? valueType;
+        Console.WriteLine(
+            $"Encountered list {field.Name} with value type {valueType.Name}!");
+
+        return field.Value.EnumerateArray()
+            .Where(key => !valueType.IsPrimitive && valueType != typeof(string))
+            .Aggregate(false, (current, key) =>
+                current | key.FindExtraJsonElementFields(valueType, field.Name)
+            );
+    }
+}
\ No newline at end of file