| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | using System.Text.Json.Nodes; |
| | | 6 | | using System.Text.Json.Schema; |
| | | 7 | | using System.Text.Json.Serialization.Metadata; |
| | | 8 | | |
| | | 9 | | namespace System.Text.Json.Serialization.Converters |
| | | 10 | | { |
| | | 11 | | internal sealed class JsonObjectConverter : JsonConverter<JsonObject?> |
| | | 12 | | { |
| | | 13 | | internal override void ConfigureJsonTypeInfo(JsonTypeInfo jsonTypeInfo, JsonSerializerOptions options) |
| | 269 | 14 | | { |
| | 269 | 15 | | jsonTypeInfo.CreateObjectForExtensionDataProperty = () => new JsonObject(options.GetNodeOptions()); |
| | 269 | 16 | | } |
| | | 17 | | |
| | | 18 | | internal override void ReadElementAndSetProperty( |
| | | 19 | | object obj, |
| | | 20 | | string propertyName, |
| | | 21 | | ref Utf8JsonReader reader, |
| | | 22 | | JsonSerializerOptions options, |
| | | 23 | | scoped ref ReadStack state) |
| | 0 | 24 | | { |
| | 0 | 25 | | bool success = JsonNodeConverter.Instance.TryRead(ref reader, typeof(JsonNode), options, ref state, out Json |
| | 0 | 26 | | Debug.Assert(success); // Node converters are not resumable. |
| | | 27 | | |
| | 0 | 28 | | Debug.Assert(obj is JsonObject); |
| | 0 | 29 | | JsonObject jObject = (JsonObject)obj; |
| | | 30 | | |
| | 0 | 31 | | Debug.Assert(value == null || value is JsonNode); |
| | 0 | 32 | | JsonNode? jNodeValue = value; |
| | | 33 | | |
| | 0 | 34 | | if (options.AllowDuplicateProperties) |
| | 0 | 35 | | { |
| | 0 | 36 | | jObject[propertyName] = jNodeValue; |
| | 0 | 37 | | } |
| | 0 | 38 | | else if (!jObject.TryAdd(propertyName, jNodeValue)) |
| | 0 | 39 | | { |
| | 0 | 40 | | ThrowHelper.ThrowJsonException_DuplicatePropertyNotAllowed(propertyName); |
| | | 41 | | } |
| | 0 | 42 | | } |
| | | 43 | | |
| | | 44 | | internal override void WriteExtensionDataValue(Utf8JsonWriter writer, JsonObject? value, JsonSerializerOptions o |
| | 0 | 45 | | { |
| | 0 | 46 | | Debug.Assert(value is not null); |
| | 0 | 47 | | value.WriteContentsTo(writer, options); |
| | 0 | 48 | | } |
| | | 49 | | |
| | | 50 | | public override void Write(Utf8JsonWriter writer, JsonObject? value, JsonSerializerOptions options) |
| | 0 | 51 | | { |
| | 0 | 52 | | if (value is null) |
| | 0 | 53 | | { |
| | 0 | 54 | | writer.WriteNullValue(); |
| | 0 | 55 | | return; |
| | | 56 | | } |
| | | 57 | | |
| | 0 | 58 | | value.WriteTo(writer, options); |
| | 0 | 59 | | } |
| | | 60 | | |
| | | 61 | | public override JsonObject? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) |
| | 634 | 62 | | { |
| | 634 | 63 | | switch (reader.TokenType) |
| | | 64 | | { |
| | | 65 | | case JsonTokenType.StartObject: |
| | 48 | 66 | | return options.AllowDuplicateProperties |
| | 48 | 67 | | ? ReadAsJsonElement(ref reader, options.GetNodeOptions()) |
| | 48 | 68 | | : ReadAsJsonNode(ref reader, options.GetNodeOptions()); |
| | | 69 | | case JsonTokenType.Null: |
| | 0 | 70 | | return null; |
| | | 71 | | default: |
| | 586 | 72 | | throw ThrowHelper.GetInvalidOperationException_ExpectedObject(reader.TokenType); |
| | | 73 | | } |
| | 0 | 74 | | } |
| | | 75 | | |
| | | 76 | | internal static JsonObject ReadAsJsonElement(ref Utf8JsonReader reader, JsonNodeOptions options) |
| | 96 | 77 | | { |
| | 96 | 78 | | JsonElement jElement = JsonElement.ParseValue(ref reader); |
| | 0 | 79 | | return new JsonObject(jElement, options); |
| | 0 | 80 | | } |
| | | 81 | | |
| | | 82 | | internal static JsonObject ReadAsJsonNode(ref Utf8JsonReader reader, JsonNodeOptions options) |
| | 0 | 83 | | { |
| | 0 | 84 | | Debug.Assert(reader.TokenType == JsonTokenType.StartObject); |
| | | 85 | | |
| | 0 | 86 | | JsonObject jObject = new JsonObject(options); |
| | | 87 | | |
| | 0 | 88 | | while (reader.Read()) |
| | 0 | 89 | | { |
| | 0 | 90 | | if (reader.TokenType == JsonTokenType.EndObject) |
| | 0 | 91 | | { |
| | 0 | 92 | | return jObject; |
| | | 93 | | } |
| | | 94 | | |
| | 0 | 95 | | if (reader.TokenType != JsonTokenType.PropertyName) |
| | 0 | 96 | | { |
| | | 97 | | // JSON is invalid so reader would have already thrown. |
| | 0 | 98 | | Debug.Fail("Property name expected."); |
| | | 99 | | ThrowHelper.ThrowJsonException(); |
| | | 100 | | } |
| | | 101 | | |
| | 0 | 102 | | string propertyName = reader.GetString()!; |
| | 0 | 103 | | reader.Read(); // Move to the value token. |
| | 0 | 104 | | JsonNode? value = JsonNodeConverter.ReadAsJsonNode(ref reader, options); |
| | | 105 | | |
| | | 106 | | // To have parity with the lazy JsonObject, we throw on duplicates. |
| | 0 | 107 | | jObject.Add(propertyName, value); |
| | 0 | 108 | | } |
| | | 109 | | |
| | | 110 | | // JSON is invalid so reader would have already thrown. |
| | 0 | 111 | | Debug.Fail("End object token not found."); |
| | | 112 | | ThrowHelper.ThrowJsonException(); |
| | | 113 | | return null; |
| | 0 | 114 | | } |
| | | 115 | | |
| | 0 | 116 | | internal override JsonSchema? GetSchema(JsonNumberHandling _) => new() { Type = JsonSchemaType.Object }; |
| | | 117 | | } |
| | | 118 | | } |