@@ -46,7 +46,7 @@ public sealed partial class JsonDocument
46
46
/// </exception>
47
47
public static JsonDocument Parse ( ReadOnlyMemory < byte > utf8Json , JsonDocumentOptions options = default )
48
48
{
49
- return Parse ( utf8Json , options . GetReaderOptions ( ) ) ;
49
+ return Parse ( utf8Json , options . GetReaderOptions ( ) , allowDuplicateProperties : options . AllowDuplicateProperties ) ;
50
50
}
51
51
52
52
/// <summary>
@@ -80,7 +80,7 @@ public static JsonDocument Parse(ReadOnlySequence<byte> utf8Json, JsonDocumentOp
80
80
81
81
if ( utf8Json . IsSingleSegment )
82
82
{
83
- return Parse ( utf8Json . First , readerOptions ) ;
83
+ return Parse ( utf8Json . First , readerOptions , allowDuplicateProperties : options . AllowDuplicateProperties ) ;
84
84
}
85
85
86
86
int length = checked ( ( int ) utf8Json . Length ) ;
@@ -89,7 +89,11 @@ public static JsonDocument Parse(ReadOnlySequence<byte> utf8Json, JsonDocumentOp
89
89
try
90
90
{
91
91
utf8Json . CopyTo ( utf8Bytes . AsSpan ( ) ) ;
92
- return Parse ( utf8Bytes . AsMemory ( 0 , length ) , readerOptions , utf8Bytes ) ;
92
+ return Parse (
93
+ utf8Bytes . AsMemory ( 0 , length ) ,
94
+ readerOptions ,
95
+ utf8Bytes ,
96
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
93
97
}
94
98
catch
95
99
{
@@ -123,7 +127,11 @@ public static JsonDocument Parse(Stream utf8Json, JsonDocumentOptions options =
123
127
Debug . Assert ( drained . Array != null ) ;
124
128
try
125
129
{
126
- return Parse ( drained . AsMemory ( ) , options . GetReaderOptions ( ) , drained . Array ) ;
130
+ return Parse (
131
+ drained . AsMemory ( ) ,
132
+ options . GetReaderOptions ( ) ,
133
+ drained . Array ,
134
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
127
135
}
128
136
catch
129
137
{
@@ -140,7 +148,8 @@ internal static JsonDocument ParseRented(PooledByteBufferWriter utf8Json, JsonDo
140
148
utf8Json . WrittenMemory ,
141
149
options . GetReaderOptions ( ) ,
142
150
extraRentedArrayPoolBytes : null ,
143
- extraPooledByteBufferWriter : utf8Json ) ;
151
+ extraPooledByteBufferWriter : utf8Json ,
152
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
144
153
}
145
154
146
155
internal static JsonDocument ParseValue ( Stream utf8Json , JsonDocumentOptions options )
@@ -157,15 +166,21 @@ internal static JsonDocument ParseValue(Stream utf8Json, JsonDocumentOptions opt
157
166
drained . AsSpan ( ) . Clear ( ) ;
158
167
ArrayPool < byte > . Shared . Return ( drained . Array ) ;
159
168
160
- return ParseUnrented ( owned . AsMemory ( ) , options . GetReaderOptions ( ) ) ;
169
+ return ParseUnrented (
170
+ owned . AsMemory ( ) ,
171
+ options . GetReaderOptions ( ) ,
172
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
161
173
}
162
174
163
175
internal static JsonDocument ParseValue ( ReadOnlySpan < byte > utf8Json , JsonDocumentOptions options )
164
176
{
165
177
byte [ ] owned = new byte [ utf8Json . Length ] ;
166
178
utf8Json . CopyTo ( owned ) ;
167
179
168
- return ParseUnrented ( owned . AsMemory ( ) , options . GetReaderOptions ( ) ) ;
180
+ return ParseUnrented (
181
+ owned . AsMemory ( ) ,
182
+ options . GetReaderOptions ( ) ,
183
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
169
184
}
170
185
171
186
internal static JsonDocument ParseValue ( string json , JsonDocumentOptions options )
@@ -209,7 +224,11 @@ private static async Task<JsonDocument> ParseAsyncCore(
209
224
Debug . Assert ( drained . Array != null ) ;
210
225
try
211
226
{
212
- return Parse ( drained . AsMemory ( ) , options . GetReaderOptions ( ) , drained . Array ) ;
227
+ return Parse (
228
+ drained . AsMemory ( ) ,
229
+ options . GetReaderOptions ( ) ,
230
+ drained . Array ,
231
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
213
232
}
214
233
catch
215
234
{
@@ -235,7 +254,10 @@ internal static async Task<JsonDocument> ParseAsyncCoreUnrented(
235
254
drained . AsSpan ( ) . Clear ( ) ;
236
255
ArrayPool < byte > . Shared . Return ( drained . Array ) ;
237
256
238
- return ParseUnrented ( owned . AsMemory ( ) , options . GetReaderOptions ( ) ) ;
257
+ return ParseUnrented (
258
+ owned . AsMemory ( ) ,
259
+ options . GetReaderOptions ( ) ,
260
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
239
261
}
240
262
241
263
/// <summary>
@@ -271,7 +293,8 @@ public static JsonDocument Parse([StringSyntax(StringSyntaxAttribute.Json)] Read
271
293
return Parse (
272
294
utf8Bytes . AsMemory ( 0 , actualByteCount ) ,
273
295
options . GetReaderOptions ( ) ,
274
- utf8Bytes ) ;
296
+ utf8Bytes ,
297
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
275
298
}
276
299
catch
277
300
{
@@ -304,7 +327,10 @@ internal static JsonDocument ParseValue(ReadOnlyMemory<char> json, JsonDocumentO
304
327
ArrayPool < byte > . Shared . Return ( utf8Bytes ) ;
305
328
}
306
329
307
- return ParseUnrented ( owned . AsMemory ( ) , options . GetReaderOptions ( ) ) ;
330
+ return ParseUnrented (
331
+ owned . AsMemory ( ) ,
332
+ options . GetReaderOptions ( ) ,
333
+ allowDuplicateProperties : options . AllowDuplicateProperties ) ;
308
334
}
309
335
310
336
/// <summary>
@@ -406,9 +432,12 @@ public static bool TryParseValue(ref Utf8JsonReader reader, [NotNullWhen(true)]
406
432
/// <exception cref="JsonException">
407
433
/// A value could not be read from the reader.
408
434
/// </exception>
409
- public static JsonDocument ParseValue ( ref Utf8JsonReader reader )
435
+ public static JsonDocument ParseValue ( ref Utf8JsonReader reader ) =>
436
+ ParseValue ( ref reader , allowDuplicateProperties : true ) ;
437
+
438
+ internal static JsonDocument ParseValue ( ref Utf8JsonReader reader , bool allowDuplicateProperties )
410
439
{
411
- bool ret = TryParseValue ( ref reader , out JsonDocument ? document , shouldThrow : true , useArrayPools : true ) ;
440
+ bool ret = TryParseValue ( ref reader , out JsonDocument ? document , shouldThrow : true , useArrayPools : true , allowDuplicateProperties ) ;
412
441
413
442
Debug . Assert ( ret , "TryParseValue returned false with shouldThrow: true." ) ;
414
443
Debug . Assert ( document != null , "null document returned with shouldThrow: true." ) ;
@@ -419,7 +448,8 @@ internal static bool TryParseValue(
419
448
ref Utf8JsonReader reader ,
420
449
[ NotNullWhen ( true ) ] out JsonDocument ? document ,
421
450
bool shouldThrow ,
422
- bool useArrayPools )
451
+ bool useArrayPools ,
452
+ bool allowDuplicateProperties = true )
423
453
{
424
454
JsonReaderState state = reader . CurrentState ;
425
455
CheckSupportedOptions ( state . Options , nameof ( reader ) ) ;
@@ -629,7 +659,7 @@ internal static bool TryParseValue(
629
659
valueSpan . CopyTo ( rentedSpan ) ;
630
660
}
631
661
632
- document = Parse ( rented . AsMemory ( 0 , length ) , state . Options , rented ) ;
662
+ document = Parse ( rented . AsMemory ( 0 , length ) , state . Options , rented , allowDuplicateProperties : allowDuplicateProperties ) ;
633
663
}
634
664
catch
635
665
{
@@ -654,7 +684,7 @@ internal static bool TryParseValue(
654
684
owned = valueSpan . ToArray ( ) ;
655
685
}
656
686
657
- document = ParseUnrented ( owned , state . Options , reader . TokenType ) ;
687
+ document = ParseUnrented ( owned , state . Options , reader . TokenType , allowDuplicateProperties : allowDuplicateProperties ) ;
658
688
}
659
689
660
690
return true ;
@@ -688,18 +718,28 @@ private static JsonDocument Parse(
688
718
ReadOnlyMemory < byte > utf8Json ,
689
719
JsonReaderOptions readerOptions ,
690
720
byte [ ] ? extraRentedArrayPoolBytes = null ,
691
- PooledByteBufferWriter ? extraPooledByteBufferWriter = null )
721
+ PooledByteBufferWriter ? extraPooledByteBufferWriter = null ,
722
+ bool allowDuplicateProperties = true )
692
723
{
693
724
ReadOnlySpan < byte > utf8JsonSpan = utf8Json . Span ;
694
725
var database = MetadataDb . CreateRented ( utf8Json . Length , convertToAlloc : false ) ;
695
726
var stack = new StackRowStack ( JsonDocumentOptions . DefaultMaxDepth * StackRow . Size ) ;
727
+ JsonDocument document ;
696
728
697
729
try
698
730
{
699
731
Parse ( utf8JsonSpan , readerOptions , ref database , ref stack ) ;
732
+ document = new JsonDocument ( utf8Json , database , extraRentedArrayPoolBytes , extraPooledByteBufferWriter , isDisposable : true ) ;
733
+
734
+ if ( ! allowDuplicateProperties )
735
+ {
736
+ ValidateNoDuplicateProperties ( document ) ;
737
+ }
700
738
}
701
739
catch
702
740
{
741
+ // The caller returns any resources they rented, so all we need to do is dispose the database.
742
+ // Specifically: don't dispose the document as that will result in double return of the rented array.
703
743
database . Dispose ( ) ;
704
744
throw ;
705
745
}
@@ -708,13 +748,14 @@ private static JsonDocument Parse(
708
748
stack . Dispose ( ) ;
709
749
}
710
750
711
- return new JsonDocument ( utf8Json , database , extraRentedArrayPoolBytes , extraPooledByteBufferWriter ) ;
751
+ return document ;
712
752
}
713
753
714
754
private static JsonDocument ParseUnrented (
715
755
ReadOnlyMemory < byte > utf8Json ,
716
756
JsonReaderOptions readerOptions ,
717
- JsonTokenType tokenType = JsonTokenType . None )
757
+ JsonTokenType tokenType = JsonTokenType . None ,
758
+ bool allowDuplicateProperties = true )
718
759
{
719
760
// These tokens should already have been processed.
720
761
Debug . Assert (
@@ -746,7 +787,14 @@ private static JsonDocument ParseUnrented(
746
787
}
747
788
}
748
789
749
- return new JsonDocument ( utf8Json , database , isDisposable : false ) ;
790
+ JsonDocument document = new JsonDocument ( utf8Json , database , isDisposable : false ) ;
791
+
792
+ if ( ! allowDuplicateProperties )
793
+ {
794
+ ValidateNoDuplicateProperties ( document ) ;
795
+ }
796
+
797
+ return document ;
750
798
}
751
799
752
800
private static ArraySegment < byte > ReadToEnd ( Stream stream )
0 commit comments