-
-
Notifications
You must be signed in to change notification settings - Fork 239
Support normalization of objects for Z.DynamicLinq.Json #958
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
a835ac8
812e0ce
bee9dea
e70bc42
643bf5a
6392b72
b9e13f3
e98ae23
9660958
c1ffa97
b372a4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace System.Linq.Dynamic.Core.NewtonsoftJson.Config; | ||
|
|
||
| /// <summary> | ||
| /// Specifies the behavior to use when setting a property vlue that does not exist or is missing during normalization. | ||
StefH marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// </summary> | ||
| public enum NormalizationNonExistingPropertyBehavior | ||
| { | ||
| /// <summary> | ||
| /// Specifies that the default value should be used. | ||
| /// </summary> | ||
| UseDefaultValue = 0, | ||
|
|
||
| /// <summary> | ||
| /// Specifies that null values should be used. | ||
| /// </summary> | ||
| UseNull = 1 | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,10 @@ | ||||||
| using Newtonsoft.Json.Linq; | ||||||
|
|
||||||
| namespace System.Linq.Dynamic.Core.NewtonsoftJson; | ||||||
|
|
||||||
| internal readonly struct JsonValueInfo(JTokenType type, object? value) | ||||||
|
||||||
| internal readonly struct JsonValueInfo(JTokenType type, object? value) | |
| internal readonly struct JsonValueInfo(JTokenType type, object? value) |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,131 @@ | ||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||
| using System.Linq.Dynamic.Core.NewtonsoftJson.Config; | ||||||||||||||||||||
| using Newtonsoft.Json.Linq; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| namespace System.Linq.Dynamic.Core.NewtonsoftJson.Utils; | ||||||||||||||||||||
|
|
||||||||||||||||||||
| internal static class NormalizeUtils | ||||||||||||||||||||
| { | ||||||||||||||||||||
| /// <summary> | ||||||||||||||||||||
| /// Normalizes an array of JSON objects so that each object contains all properties found in the array, | ||||||||||||||||||||
| /// including nested objects. Missing properties will have null values. | ||||||||||||||||||||
| /// </summary> | ||||||||||||||||||||
| internal static JArray NormalizeArray(JArray jsonArray, NormalizationNonExistingPropertyBehavior normalizationBehavior) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| if (jsonArray.Any(item => item is not JObject)) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| return jsonArray; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| var schema = BuildSchema(jsonArray); | ||||||||||||||||||||
| var normalizedArray = new JArray(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| foreach (var jo in jsonArray.OfType<JObject>()) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| var normalizedObj = NormalizeObject(jo, schema, normalizationBehavior); | ||||||||||||||||||||
| normalizedArray.Add(normalizedObj); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| return normalizedArray; | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| private static Dictionary<string, JsonValueInfo> BuildSchema(JArray array) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| var schema = new Dictionary<string, JsonValueInfo>(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| foreach (var item in array) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| if (item is JObject obj) | ||||||||||||||||||||
| { | ||||||||||||||||||||
| MergeSchema(schema, obj); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
Comment on lines
+36
to
+41
|
||||||||||||||||||||
| foreach (var item in array) | |
| { | |
| if (item is JObject obj) | |
| { | |
| MergeSchema(schema, obj); | |
| } | |
| foreach (var obj in array.OfType<JObject>()) | |
| { | |
| MergeSchema(schema, obj); |
Copilot
AI
Nov 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient use of 'ContainsKey' and indexer.
| result[key] = source.ContainsKey(key) && source[key] is JObject jo ? NormalizeObject(jo, nestedSchema, normalizationBehavior) : CreateEmptyObject(nestedSchema, normalizationBehavior); | |
| var value = source[key]; | |
| result[key] = value is JObject jo ? NormalizeObject(jo, nestedSchema, normalizationBehavior) : CreateEmptyObject(nestedSchema, normalizationBehavior); |
Copilot
AI
Nov 15, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient use of 'ContainsKey' and indexer.
| if (source.ContainsKey(key)) | |
| { | |
| result[key] = source[key]; | |
| if (source.TryGetValue(key, out var value)) | |
| { | |
| result[key] = value; |
StefH marked this conversation as resolved.
Show resolved
Hide resolved
StefH marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,17 @@ | ||||||
| namespace System.Linq.Dynamic.Core.SystemTextJson.Config; | ||||||
|
|
||||||
| /// <summary> | ||||||
| /// Specifies the behavior to use when setting a property vlue that does not exist or is missing during normalization. | ||||||
|
||||||
| /// Specifies the behavior to use when setting a property vlue that does not exist or is missing during normalization. | |
| /// Specifies the behavior to use when setting a property value that does not exist or is missing during normalization. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||||||||||||||||||
| #if !NET8_0_OR_GREATER | ||||||||||||||||||||||
| namespace System.Text.Json.Nodes; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| internal static class JsonValueExtensions | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| internal static JsonValueKind? GetValueKind(this JsonNode node) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| if (node is JsonObject) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| return JsonValueKind.Object; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if (node is JsonArray) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| return JsonValueKind.Array; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return node.GetValue<object>() is JsonElement je ? je.ValueKind : null; | ||||||||||||||||||||||
|
||||||||||||||||||||||
| return node.GetValue<object>() is JsonElement je ? je.ValueKind : null; | |
| if (node is JsonValue jsonValue) | |
| { | |
| var value = jsonValue.GetValue<object>(); | |
| if (value is JsonElement je) | |
| { | |
| return je.ValueKind; | |
| } | |
| } | |
| return null; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| using System.Text.Json; | ||
|
|
||
| namespace System.Linq.Dynamic.Core.SystemTextJson; | ||
|
|
||
| internal readonly struct JsonValueInfo(JsonValueKind type, object? value) | ||
| { | ||
| public JsonValueKind Type { get; } = type; | ||
|
|
||
| public object? Value { get; } = value; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1093,13 +1093,20 @@ private static JsonDocument ToJsonDocumentArray(Func<IQueryable> func) | |||||||||||||||||||||||||||||||||||||
| // ReSharper disable once UnusedParameter.Local | ||||||||||||||||||||||||||||||||||||||
| private static IQueryable ToQueryable(JsonDocument source, SystemTextJsonParsingConfig? config = null) | ||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||
| config = config ?? SystemTextJsonParsingConfig.Default; | ||||||||||||||||||||||||||||||||||||||
| config.ConvertObjectToSupportComparison = true; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
| config.ConvertObjectToSupportComparison = true; | |
| // Avoid mutating the caller's config object | |
| if (ReferenceEquals(config, SystemTextJsonParsingConfig.Default)) | |
| { | |
| // Default config is safe to mutate | |
| config.ConvertObjectToSupportComparison = true; | |
| } | |
| else | |
| { | |
| // Create a copy to avoid mutating the caller's config | |
| config = new SystemTextJsonParsingConfig | |
| { | |
| Normalize = config.Normalize, | |
| NormalizationNonExistingPropertyValueBehavior = config.NormalizationNonExistingPropertyValueBehavior, | |
| ConvertObjectToSupportComparison = true | |
| // Copy other properties as needed | |
| }; | |
| } |
Uh oh!
There was an error while loading. Please reload this page.