Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
fix part 2
  • Loading branch information
StefH committed Nov 22, 2025
commit b71c2a3e28ae56787e5a41915b6ee3725e514d5e
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public class NewtonsoftJsonParsingConfig : ParsingConfig
/// <summary>
/// The default ParsingConfig for <see cref="NewtonsoftJsonParsingConfig"/>.
/// </summary>
public new static NewtonsoftJsonParsingConfig Default { get; } = new();
public new static NewtonsoftJsonParsingConfig Default { get; } = new NewtonsoftJsonParsingConfig
{
ConvertObjectToSupportComparison = true
};

/// <summary>
/// The default <see cref="DynamicJsonClassOptions"/> to use.
Expand All @@ -28,7 +31,7 @@ public class NewtonsoftJsonParsingConfig : ParsingConfig
/// <remarks>
/// Use this property to control how the normalization process handles properties that are missing or undefined.
/// The selected behavior may affect the output or error handling of normalization operations.
/// The default value is <see cref="NormalizationNonExistingPropertyBehavior.UseDefaultValue"/>.
/// The default value is <see cref="NormalizationNonExistingPropertyBehavior.UseNull"/>.
/// </remarks>
public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
public enum NormalizationNonExistingPropertyBehavior
{
/// <summary>
/// Specifies that the default value should be used.
/// Specifies that a null value should be used.
/// </summary>
UseDefaultValue = 0,
UseNull = 0,

/// <summary>
/// Specifies that null values should be used.
/// Specifies that the default value should be used.
/// </summary>
UseNull = 1
UseDefaultValue = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ private class JTokenResolvers : Dictionary<JTokenType, Func<JToken, DynamicJsonC
foreach (var prop in src.Properties())
{
var value = Resolvers[prop.Type](prop.Value, options);
if (value != null)
{
dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value));
}
dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value));
}

return DynamicClassFactory.CreateInstance(dynamicPropertiesWithValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -871,8 +871,11 @@ private static JArray ToJArray(Func<IQueryable> func)

private static IQueryable ToQueryable(JArray source, NewtonsoftJsonParsingConfig? config = null)
{
var normalized = config?.Normalize == true ?
NormalizeUtils.NormalizeArray(source, config.NormalizationNonExistingPropertyValueBehavior):
config = config ?? NewtonsoftJsonParsingConfig.Default;
config.ConvertObjectToSupportComparison = true;

var normalized = config.Normalize == true ?
NormalizeUtils.NormalizeArray(source, config.NormalizationNonExistingPropertyValueBehavior) :
source;

return normalized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private static JObject NormalizeObject(JObject source, Dictionary<string, JsonVa
}
else
{
result[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(schema[key]) : JValue.CreateNull();
result[key] = GetDefaultOrNullValue(normalizationBehavior, schema[key]);
}
}
}
Expand All @@ -105,7 +105,7 @@ private static JObject CreateEmptyObject(Dictionary<string, JsonValueInfo> schem
}
else
{
obj[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(schema[key]) : JValue.CreateNull();
obj[key] = GetDefaultOrNullValue(normalizationBehavior, schema[key]);
}
}

Expand All @@ -125,7 +125,28 @@ private static JToken GetDefaultValue(JsonValueInfo jType)
JTokenType.Integer => default(int),
JTokenType.String => string.Empty,
JTokenType.TimeSpan => TimeSpan.MinValue,
_ => GetNullValue(jType),
};
}

private static JValue GetNullValue(JsonValueInfo jType)
{
return jType.Type switch
{
JTokenType.Boolean => new JValue((bool?)null),
JTokenType.Bytes => new JValue((byte[]?)null),
JTokenType.Date => new JValue((DateTime?)null),
JTokenType.Float => new JValue((float?)null),
JTokenType.Guid => new JValue((Guid?)null),
JTokenType.Integer => new JValue((int?)null),
JTokenType.String => new JValue((string?)null),
JTokenType.TimeSpan => new JValue((TimeSpan?)null),
_ => JValue.CreateNull(),
};
}

private static JToken GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
{
return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
public enum NormalizationNonExistingPropertyBehavior
{
/// <summary>
/// Specifies that the default value should be used.
/// Specifies that a null value should be used.
/// </summary>
UseDefaultValue = 0,
UseNull = 0,

/// <summary>
/// Specifies that null values should be used.
/// Specifies that the default value should be used.
/// </summary>
UseNull = 1
UseDefaultValue = 1
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class SystemTextJsonParsingConfig : ParsingConfig
/// <remarks>
/// Use this property to control how the normalization process handles properties that are missing or undefined.
/// The selected behavior may affect the output or error handling of normalization operations.
/// The default value is <see cref="NormalizationNonExistingPropertyBehavior.UseDefaultValue"/>.
/// The default value is <see cref="NormalizationNonExistingPropertyBehavior.UseNull"/>.
/// </remarks>
public NormalizationNonExistingPropertyBehavior NormalizationNonExistingPropertyValueBehavior { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,7 @@ private class JTokenResolvers : Dictionary<JsonValueKind, Func<JsonElement, Dyna
foreach (var prop in src.Value.EnumerateObject())
{
var value = Resolvers[prop.Value.ValueKind](prop.Value, options);
if (value != null)
{
dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value));
}
dynamicPropertiesWithValue.Add(new DynamicPropertyWithValue(prop.Name, value));
}

return DynamicClassFactory.CreateInstance(dynamicPropertiesWithValue);
Expand Down Expand Up @@ -129,7 +126,7 @@ private static IEnumerable ConvertJsonElementToEnumerable(JsonElement arg, Dynam
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
{
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
return (IEnumerable)method.Invoke(null, new object[] { src })!;
return (IEnumerable)method.Invoke(null, [src])!;
}

private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JsonDocumentExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private static JsonObject NormalizeObject(JsonObject source, Dictionary<string,
}
else
{
result[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : null;
result[key] = GetDefaultOrNullValue(normalizationBehavior, jType);
}
}
}
Expand All @@ -135,7 +135,7 @@ private static JsonObject CreateEmptyObject(Dictionary<string, JsonValueInfo> sc
}
else
{
obj[key] = normalizationBehavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : null;
obj[key] = GetDefaultOrNullValue(normalizationBehavior, jType);
}
}

Expand All @@ -151,7 +151,25 @@ private static JsonObject CreateEmptyObject(Dictionary<string, JsonValueInfo> sc
JsonValueKind.Number => default(int),
JsonValueKind.String => string.Empty,
JsonValueKind.True => false,
_ => GetNullValue(jType),
};
}

private static JsonNode? GetNullValue(JsonValueInfo jType)
{
return jType.Type switch
{
JsonValueKind.Array => null,
JsonValueKind.False => JsonValue.Create<bool?>(false),
JsonValueKind.Number => JsonValue.Create<int?>(null),
JsonValueKind.String => JsonValue.Create<string?>(null),
JsonValueKind.True => JsonValue.Create<bool?>(true),
_ => null,
};
}

private static JsonNode? GetDefaultOrNullValue(NormalizationNonExistingPropertyBehavior behavior, JsonValueInfo jType)
{
return behavior == NormalizationNonExistingPropertyBehavior.UseDefaultValue ? GetDefaultValue(jType) : GetNullValue(jType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,6 @@ public void NormalizeArray_When_NormalizeIsFalse_ShouldThrow()
Action act = () => JArray.Parse(array).Where(config, "Age >= 30");

// Assert
act.Should().Throw<InvalidOperationException>().WithMessage("The binary operator GreaterThanOrEqual is not defined for the types 'System.Object' and 'System.Int32'.");
act.Should().Throw<InvalidOperationException>().WithMessage("Unable to find property 'Age' on type '<>*");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,20 @@ public void Distinct()
public void First()
{
// Act + Assert 1
_source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
_source.First().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 2
_source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.First("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}

[Fact]
public void FirstOrDefault()
{
// Act + Assert 1
_source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30}").RootElement.GetRawText());
_source.FirstOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""John"",""Age"":30,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 2
_source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.FirstOrDefault("Age > 30")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 3
_source.FirstOrDefault("Age > 999").Should().BeNull();
Expand Down Expand Up @@ -269,20 +269,20 @@ public void GroupBySimpleKeySelector()
public void Last()
{
// Act + Assert 1
_source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.Last().GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 2
_source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.Last("Age > 0").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}

[Fact]
public void LastOrDefault()
{
// Act + Assert 1
_source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.LastOrDefault()!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 2
_source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.LastOrDefault("Age > 0")!.Value.GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());

// Act + Assert 3
_source.LastOrDefault("Age > 999").Should().BeNull();
Expand Down Expand Up @@ -446,7 +446,7 @@ public void SelectMany()
public void Single()
{
// Act + Assert
_source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40}").RootElement.GetRawText());
_source.Single("Age > 30").GetRawText().Should().BeEquivalentTo(JsonDocument.Parse(@"{""Name"":""Doe"",""Age"":40,""IsNull"":null,""AlsoNull"":null}").RootElement.GetRawText());
}

[Fact]
Expand Down
Loading