@@ -14,56 +14,55 @@ static class SystemTextJsonUtils
1414 /// <summary> Returns the property path to an unfinished JSON value </summary>
1515 public static string [ ] GetFailurePath ( ReadOnlySpan < byte > data )
1616 {
17- // TODO: tests
18- var reader = new Utf8JsonReader ( data , false , default ) ;
19- var path = new Stack < ( string ? name , int index ) > ( ) ;
20- var isArray = false ;
21- int arrayIndex = 0 ;
22- while ( reader . Read ( ) )
17+ // configure higher max depth than default (64), to correctly display path of
18+ // the "max depth exceeded" error
19+ var options = new JsonReaderOptions { MaxDepth = 196 } ;
20+ var reader = new Utf8JsonReader ( data , false , new JsonReaderState ( options ) ) ;
21+ if ( ! reader . Read ( ) )
22+ return [ ] ;
23+ return GetFailurePathInternal ( ref reader ) ?? throw new Exception ( "No error in specified JSON" ) ;
24+ }
25+
26+ private static string [ ] ? GetFailurePathInternal ( ref Utf8JsonReader reader )
27+ {
28+ if ( reader . TokenType == JsonTokenType . None )
29+ return [ ] ;
30+ else if ( reader . TokenType == JsonTokenType . StartObject )
31+ {
32+ if ( ! reader . Read ( ) ) return [ ] ;
33+ string ? lastProperty = null ;
34+ while ( reader . TokenType == JsonTokenType . PropertyName )
35+ {
36+ lastProperty = reader . GetString ( ) . NotNull ( ) ;
37+ if ( ! reader . Read ( ) )
38+ return [ lastProperty ] ;
39+ if ( GetFailurePathInternal ( ref reader ) is { } nestedError )
40+ return [ lastProperty , ..nestedError ] ;
41+ if ( ! reader . Read ( ) )
42+ return [ ] ;
43+ }
44+ if ( reader . TokenType != JsonTokenType . EndObject )
45+ return lastProperty is null ? [ ] : [ lastProperty ] ;
46+ return null ;
47+ }
48+ else if ( reader . TokenType == JsonTokenType . StartArray )
2349 {
24- switch ( reader . TokenType )
50+ if ( ! reader . Read ( ) ) return [ "0" ] ;
51+ int index = 0 ;
52+ while ( reader . TokenType != JsonTokenType . EndArray )
2553 {
26- case JsonTokenType . StartObject :
27- if ( isArray ) {
28- isArray = false ;
29- path . Push ( ( null , arrayIndex ) ) ;
30- }
31- break ;
32- case JsonTokenType . Comment :
33- break ;
34- case JsonTokenType . StartArray :
35- isArray = true ;
36- arrayIndex = 0 ;
37- break ;
38- case JsonTokenType . EndArray :
39- isArray = false ;
40- break ;
41- case JsonTokenType . True :
42- case JsonTokenType . False :
43- case JsonTokenType . Number :
44- case JsonTokenType . String :
45- case JsonTokenType . Null :
46- case JsonTokenType . EndObject :
47- if ( ! isArray ) {
48- var old = path . Pop ( ) ;
49- if ( old . name is null ) {
50- isArray = true ;
51- arrayIndex = old . index + 1 ;
52- }
53- }
54- else {
55- arrayIndex ++ ;
56- }
57- break ;
58- case JsonTokenType . PropertyName :
59- path . Push ( ( reader . GetString ( ) ! , - 1 ) ) ;
60- break ;
61- case JsonTokenType . None :
62- goto Done ;
54+ if ( GetFailurePathInternal ( ref reader ) is { } nestedError )
55+ return [ $ "{ index } ", ..nestedError ] ;
56+ index ++ ;
57+ if ( ! reader . Read ( ) || reader . TokenType == JsonTokenType . None )
58+ return [ $ "{ index } "] ;
6359 }
60+ return null ;
61+ }
62+ else
63+ {
64+ return null ;
6465 }
65- Done :
66- return path . Reverse ( ) . Select ( n => n . name ?? n . index . ToString ( ) ) . ToArray ( ) ;
6766 }
6867
6968 public static JsonElement ? GetPropertyOrNull ( this in JsonElement jsonObj , ReadOnlySpan < byte > name ) =>
0 commit comments