Skip to content
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

Make JSON support required properties #72937

Merged
merged 11 commits into from
Jul 29, 2022
8 changes: 4 additions & 4 deletions src/libraries/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -660,12 +660,12 @@
<value>Parameter already associated with a different JsonTypeInfo instance.</value>
</data>
<data name="JsonPropertyRequiredAndNotDeserializable" xml:space="preserve">
<value>JsonPropertyInfo with name '{0}' for type '{1}' is required but is not deserializable.</value>
<value>JsonPropertyInfo '{0}' defined in type '{1}' is marked required but does not specify a setter.</value>
</data>
<data name="JsonPropertyRequiredAndExtensionData" xml:space="preserve">
<value>JsonPropertyInfo with name '{0}' for type '{1}' cannot be required and be an extension data property.</value>
<value>JsonPropertyInfo '{0}' defined in type '{1}' is marked both as required and as an extension data property. This combination is not supported.</value>
</data>
<data name="JsonRequiredPropertiesMissing" xml:space="preserve">
<value>Type '{0}' contains required properties which were not set during deserialization: {1}</value>
<value>JSON deserialization for type '{0}' was missing required properties, including the following: {1}</value>
</data>
</root>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public JsonTypeInfo BaseJsonTypeInfo
// Whether to use custom number handling.
public JsonNumberHandling? NumberHandling;

// Required properties left
public BitArray? RequiredPropertiesLeft;
// Required properties which have value assigned
krwq marked this conversation as resolved.
Show resolved Hide resolved
public BitArray? RequiredPropertiesSet;

public void EndConstructorParameter()
{
Expand Down Expand Up @@ -117,19 +117,19 @@ public void MarkRequiredPropertyAsRead(JsonPropertyInfo propertyInfo)
{
if (propertyInfo.IsRequired)
krwq marked this conversation as resolved.
Show resolved Hide resolved
{
Debug.Assert(RequiredPropertiesLeft != null);
RequiredPropertiesLeft[propertyInfo.RequiredPropertyIndex] = false;
Debug.Assert(RequiredPropertiesSet != null);
RequiredPropertiesSet[propertyInfo.RequiredPropertyIndex] = true;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void InitializeRequiredPropertiesValidationState(JsonTypeInfo typeInfo)
{
Debug.Assert(RequiredPropertiesLeft == null);
Debug.Assert(RequiredPropertiesSet == null);

if (typeInfo.NumberOfRequiredProperties > 0)
{
RequiredPropertiesLeft = new BitArray(typeInfo.NumberOfRequiredProperties, defaultValue: true);
RequiredPropertiesSet = new BitArray(typeInfo.NumberOfRequiredProperties, defaultValue: false);
krwq marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand All @@ -138,11 +138,11 @@ internal void ValidateAllRequiredPropertiesAreRead(JsonTypeInfo typeInfo)
{
if (typeInfo.NumberOfRequiredProperties > 0)
{
Debug.Assert(RequiredPropertiesLeft != null);
Debug.Assert(RequiredPropertiesSet != null);

if (!RequiredPropertiesLeft.AllBitsEqual(false))
if (!RequiredPropertiesSet.AllBitsEqual(true))
{
ThrowHelper.ThrowJsonException_JsonRequiredPropertyMissing(typeInfo, RequiredPropertiesLeft);
ThrowHelper.ThrowJsonException_JsonRequiredPropertyMissing(typeInfo, RequiredPropertiesSet);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,18 +216,21 @@ public static void ThrowInvalidOperationException_JsonPropertyRequiredAndExtensi
}

[DoesNotReturn]
public static void ThrowJsonException_JsonRequiredPropertyMissing(JsonTypeInfo parent, BitArray missingJsonProperties)
public static void ThrowJsonException_JsonRequiredPropertyMissing(JsonTypeInfo parent, BitArray requiredPropertiesSet)
{
StringBuilder listOfMissingPropertiesBuilder = new();
bool first = true;

Debug.Assert(parent.PropertyCache != null);

// Soft cut-off length - once message becomes longer than that we won't be adding more elements
const int CutOffLength = 50;

for (int propertyIdx = 0; propertyIdx < parent.PropertyCache.List.Count; propertyIdx++)
{
JsonPropertyInfo property = parent.PropertyCache.List[propertyIdx].Value;

if (!property.IsRequired || !missingJsonProperties[property.RequiredPropertyIndex])
if (!property.IsRequired || requiredPropertiesSet[property.RequiredPropertyIndex])
{
continue;
}
Expand All @@ -240,6 +243,11 @@ public static void ThrowJsonException_JsonRequiredPropertyMissing(JsonTypeInfo p

listOfMissingPropertiesBuilder.Append(property.Name);
krwq marked this conversation as resolved.
Show resolved Hide resolved
first = false;

if (listOfMissingPropertiesBuilder.Length >= CutOffLength)
{
break;
}
}

throw new JsonException(SR.Format(SR.JsonRequiredPropertiesMissing, parent.Type, listOfMissingPropertiesBuilder.ToString()));
Expand Down