-
-
Notifications
You must be signed in to change notification settings - Fork 244
Expand file tree
/
Copy pathJsonDocumentExtensions.cs
More file actions
138 lines (112 loc) · 4.48 KB
/
JsonDocumentExtensions.cs
File metadata and controls
138 lines (112 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using JsonConverter.Abstractions.Models;
namespace System.Linq.Dynamic.Core.SystemTextJson.Extensions;
internal static class JsonDocumentExtensions
{
private sealed class JTokenResolvers : Dictionary<JsonValueKind, Func<JsonElement, DynamicJsonClassOptions?, object?>>;
private static readonly JTokenResolvers Resolvers = new()
{
{ JsonValueKind.Array, ConvertJsonElementToEnumerable },
{ JsonValueKind.False, (_, _) => false },
{ JsonValueKind.True, (_, _) => true },
{ JsonValueKind.Number, ConvertNumber },
{ JsonValueKind.Null, (_, _) => null },
{ JsonValueKind.Object, ConvertJsonElement },
{ JsonValueKind.String, ConvertString },
{ JsonValueKind.Undefined, (_, _) => null }
};
internal static DynamicClass? ToDynamicClass(this JsonElement? src, DynamicJsonClassOptions? options = null)
{
if (src == null)
{
return null;
}
var dynamicPropertiesWithValue = new List<DynamicPropertyWithValue>();
foreach (var prop in src.Value.EnumerateObject())
{
var value = Resolvers[prop.Value.ValueKind](prop.Value, options);
dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value));
}
return DynamicClassFactory.CreateInstance(dynamicPropertiesWithValue);
}
public static IEnumerable ToDynamicJsonClassArray(this JsonElement? src, DynamicJsonClassOptions? options = null)
{
return src == null ? Array.Empty<object?>() : ConvertJsonElementToEnumerable(src.Value, options);
}
private static object? ConvertJsonElement(JsonElement arg, DynamicJsonClassOptions? options = null)
{
return arg.ValueKind == JsonValueKind.Object ? ToDynamicClass(arg, options) : GetResolverFor(arg)(arg, options);
}
private static object PassThrough(JsonElement arg, DynamicJsonClassOptions? options)
{
return arg;
}
private static Func<JsonElement, DynamicJsonClassOptions?, object?> GetResolverFor(JsonElement arg)
{
return Resolvers.TryGetValue(arg.ValueKind, out var result) ? result : PassThrough;
}
private static object? ConvertString(JsonElement arg, DynamicJsonClassOptions? options = null)
{
if (arg.TryGetGuid(out var guid))
{
return guid;
}
if (arg.TryGetDateTime(out var dt))
{
return dt;
}
return arg.GetString();
}
private static object ConvertNumber(JsonElement arg, DynamicJsonClassOptions? options = null)
{
if (arg.TryGetInt32(out var int32))
{
return int32;
}
if (arg.TryGetInt64(out var int64))
{
return int64;
}
if (arg.TryGetDouble(out var @double))
{
return @double;
}
if (arg.TryGetDecimal(out var @decimal))
{
return @decimal;
}
if (arg.TryGetByte(out var @byte))
{
return @byte;
}
throw new InvalidOperationException($"Unable to convert {nameof(JsonElement)} of type: {arg.ValueKind} to int, long, double, decimal or byte.");
}
private static IEnumerable ConvertJsonElementToEnumerable(JsonElement arg, DynamicJsonClassOptions? options = null)
{
var result = new List<object?>();
foreach (var item in arg.EnumerateArray())
{
result.Add(ConvertJsonElement(item));
}
var distinctType = FindSameTypeOf(result);
return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType);
}
private static Type? FindSameTypeOf(IEnumerable<object?> src)
{
var types = src.Select(o => o?.GetType()).Distinct().OfType<Type>().ToArray();
return types.Length == 1 ? types[0] : null;
}
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
{
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
return (IEnumerable)method.Invoke(null, [src])!;
}
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
private static T[] ConvertToTypedArrayGeneric<T>(IEnumerable<object> src)
{
return src.Cast<T>().ToArray();
}
}