11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . Linq ;
45using System . Text ;
56using Newtonsoft . Json ;
67using Newtonsoft . Json . Linq ;
@@ -53,23 +54,34 @@ public void Remove(string key)
5354 jValue ? . Remove ( ) ;
5455 }
5556
56- internal static IEnumerable < KeyValuePair < string , JValue > > AllValues ( JToken token , string prefix = "" )
57+ internal static IEnumerable < KeyValuePair < string , JValue > > AllValues ( JToken token )
5758 {
59+ return AllTokens ( token )
60+ . Where ( kv => kv . Value is JValue )
61+ . Select ( kv => KeyValuePair . Create ( kv . Key , ( kv . Value as JValue ) ! ) ) ;
62+ }
63+
64+ internal static IEnumerable < KeyValuePair < string , JToken > > AllTokens ( JToken token , string prefix = "" )
65+ {
66+ yield return KeyValuePair . Create ( prefix , token ) ;
67+
5868 switch ( token )
5969 {
60- case JValue jValue :
61- yield return KeyValuePair . Create ( prefix , jValue ) ;
70+ case JValue :
6271 break ;
6372
6473 case JProperty jProperty :
65- foreach ( var kv in AllValues ( jProperty . Value , CreatePrefix ( prefix , jProperty . Name ) ) )
74+ foreach ( var kv in AllTokens ( jProperty . Value , CreatePrefix ( prefix , jProperty . Name ) ) )
6675 yield return kv ;
6776 break ;
6877
6978 case JArray jArray :
7079 for ( var i = 0 ; i < jArray . Count ; i ++ )
71- foreach ( var kv in AllValues ( jArray [ i ] , CreatePrefix ( prefix , i . ToString ( ) ) ) )
80+ {
81+ foreach ( var kv in AllTokens ( jArray [ i ] , CreatePrefix ( prefix , i . ToString ( ) ) ) )
7282 yield return kv ;
83+ }
84+
7385 break ;
7486
7587 case JObject jObject :
@@ -78,7 +90,7 @@ internal static IEnumerable<KeyValuePair<string, JValue>> AllValues(JToken token
7890 if ( value == null )
7991 continue ;
8092
81- foreach ( var kv in AllValues ( value , CreatePrefix ( prefix , key ) ) )
93+ foreach ( var kv in AllTokens ( value , CreatePrefix ( prefix , key ) ) )
8294 yield return kv ;
8395 }
8496
@@ -101,62 +113,69 @@ internal static IEnumerable<KeyValuePair<string, JValue>> AllValues(JToken token
101113
102114 internal JToken ? FindToken ( string key , bool createNew = false )
103115 {
104- var subKeys = string . IsNullOrWhiteSpace ( key ) ? new string [ 0 ] : key . Split ( ':' ) ;
116+ var parentKey = "" ;
117+ var bestParent = ( JToken ? ) ( _json as JObject ) ?? ( _json as JArray ) ;
105118
106- var current = _json ;
107- for ( var i = 0 ; i < subKeys . Length ; i ++ )
119+ foreach ( var kv in AllTokens ( _json ) )
108120 {
109- var subkey = subKeys [ i ] ;
110- var isLastKey = i == subKeys . Length - 1 ;
111-
112- if ( current is JValue && ! createNew )
113- return null ;
121+ if ( kv . Key == key )
122+ return kv . Value ;
114123
115- if ( int . TryParse ( subkey , out _ ) && current is JObject && ( ( JObject ) current ) . Count == 0 && current . Parent != null )
124+ if ( key . StartsWith ( kv . Key ) && kv . Value is JObject jObject && kv . Key . Length > parentKey . Length )
116125 {
117- if ( current . Parent is JProperty jProperty )
118- jProperty . Value = current = new JArray ( ) ;
119- else if ( current . Parent is JArray parentArray )
120- parentArray [ parentArray . IndexOf ( current ) ] = current = new JArray ( ) ;
126+ parentKey = kv . Key ;
127+ bestParent = jObject ;
121128 }
129+ }
122130
123- if ( current is JArray jArray )
124- {
125- current = FindTokenInArray ( jArray , subkey , createNew , isLastKey ) ;
126- continue ;
127- }
131+ if ( bestParent == null || ! createNew )
132+ return null ;
128133
129- if ( current is JValue )
130- return null ;
134+ var restKeys = key . Substring ( parentKey . Length ) . TrimStart ( ':' ) . Split ( ':' ) ;
135+ for ( var i = 0 ; i < restKeys . Length - 1 ; i ++ )
136+ {
137+ JToken newValue ;
131138
132- var jObject = ( JObject ) current ! ; // At this point current can only be a JObject.
133- current = jObject [ subkey ] ;
139+ if ( restKeys . Length > i + 1 && int . TryParse ( restKeys [ i + 1 ] , out _ ) )
140+ newValue = new JArray ( ) ;
141+ else
142+ newValue = new JObject ( ) ;
134143
135- if ( createNew && ( current is null || ( current is JValue && ! isLastKey ) ) )
144+ if ( bestParent is JArray jArray )
136145 {
137- current = jObject [ subkey ] = isLastKey ? ( JToken ) new JValue ( ( object ? ) null ) : new JObject ( ) ;
146+ if ( ! int . TryParse ( restKeys [ i ] , out var idx ) )
147+ throw new Exception ( $ "Cannot index into array with key '{ restKeys [ ^ 1 ] } '") ;
148+
149+ while ( jArray . Count <= idx )
150+ jArray . Add ( new JValue ( ( object ? ) null ) ) ;
151+ jArray [ idx ] = newValue ;
152+ }
153+ else
154+ {
155+ bestParent [ restKeys [ i ] ] = newValue ;
138156 }
157+
158+ bestParent = newValue ;
139159 }
140160
141- return current ; // If current is no JValue here, throw an exception
142- }
161+ var value = ( JToken ) new JValue ( ( object ? ) null ) ;
143162
144- internal static JToken FindTokenInArray ( JArray jArray , string subkey , bool createNew , bool isLastKey )
145- {
146- if ( ! int . TryParse ( subkey , out var index ) )
147- throw new Exception ( $ "Cannot index into array at { GetPosition ( jArray ) } with index { subkey } . ") ;
163+ if ( bestParent is JArray array )
164+ {
165+ if ( ! int . TryParse ( restKeys [ ^ 1 ] , out var idx ) )
166+ throw new Exception ( $ "Cannot index into array with key ' { restKeys [ ^ 1 ] } ' ") ;
148167
149- if ( createNew && index >= jArray . Count )
168+ while ( array . Count <= idx )
169+ array . Add ( new JValue ( ( object ? ) null ) ) ;
170+ array [ idx ] = value ;
171+ value = array [ idx ] ;
172+ }
173+ else
150174 {
151- for ( var j = jArray . Count ; j < index ; j ++ )
152- jArray . Add ( new JValue ( ( object ? ) null ) ) ;
153- jArray . Add ( isLastKey ? ( JToken ) new JValue ( ( object ? ) null ) : new JObject ( ) ) ;
175+ bestParent [ restKeys [ ^ 1 ] ] = value ;
154176 }
155177
156- if ( index >= jArray . Count )
157- throw new IndexOutOfRangeException ( $ "Index { index } does not exist for array at { GetPosition ( jArray ) } ") ;
158-
159- return jArray [ index ] ;
178+ return value ;
160179 }
161180
162181 internal void SetValue ( string key , object ? value )
@@ -168,8 +187,5 @@ internal void SetValue(string key, object? value)
168187
169188 jValue . Value = value ;
170189 }
171-
172- internal static string GetPosition ( JToken token )
173- => token . Path . Replace ( "." , ":" ) . Replace ( "[" , "." ) . Replace ( "]" , "" ) ;
174190 }
175191}
0 commit comments