1+ #nullable enable
12using System . Collections . Generic ;
23using System . Linq ;
34
@@ -16,71 +17,139 @@ public static class JsonHelper
1617 /// <param name="oldJson">The old JSON object</param>
1718 /// <param name="currentJson">The new JSON object</param>
1819 /// <returns>Key-value pairs of the changed fields</returns>
19- public static Dictionary < string , object > FindChangedFields ( string oldJson , string currentJson )
20+ public static Dictionary < string , object ? > FindChangedFields ( string oldJson , string currentJson )
2021 {
2122 JToken old = JToken . Parse ( oldJson ) ;
2223 JToken current = JToken . Parse ( currentJson ) ;
23- Dictionary < string , object > dict = new Dictionary < string , object > ( ) ;
24+ Dictionary < string , object ? > dict = new Dictionary < string , object ? > ( ) ;
2425 FindDiff ( dict , old , current , string . Empty ) ;
2526 return dict ;
2627 }
2728
28- private static void FindDiff ( Dictionary < string , object > dict , JToken Old , JToken Current , string prefix )
29+ private static void FindDiff ( Dictionary < string , object ? > dict , JToken ? old , JToken ? current , string prefix )
2930 {
30- if ( JToken . DeepEquals ( Old , Current ) )
31+ if ( JToken . DeepEquals ( old , current ) )
3132 {
3233 return ;
3334 }
3435
35- switch ( Current . Type )
36+ int index = 0 ;
37+ JArray ? oldArray = old as JArray ;
38+ JObject ? currentObj = current as JObject ;
39+ JObject ? oldObj = old as JObject ;
40+
41+ switch ( current ? . Type )
3642 {
3743 case JTokenType . Object :
38- JObject current = Current as JObject ;
39- JObject old = Old as JObject ;
4044
41- if ( old == null )
45+ if ( oldArray != null )
46+ {
47+ for ( index = 0 ; index < oldArray . Count ; index ++ )
48+ {
49+ dict . Add ( $ "{ prefix } [{ index } ]", null ) ;
50+ }
51+ }
52+ else if ( old ? . Type != JTokenType . Object )
53+ {
54+ // Scalar values would use the plain prefix, but object create deeper prefixes. If a scalar
55+ // value is replaced by an object, we need to unset the scalar value as well.
56+ dict . Add ( prefix , null ) ;
57+ }
58+
59+ if ( oldObj == null && currentObj != null )
4260 {
43- foreach ( string key in current . Properties ( ) . Select ( c => c . Name ) )
61+ foreach ( string key in currentObj . Properties ( ) . Select ( c => c . Name ) )
4462 {
45- FindDiff ( dict , new JObject ( ) , current [ key ] , Join ( prefix , key ) ) ;
63+ FindDiff ( dict , JValue . CreateNull ( ) , currentObj [ key ] , Join ( prefix , key ) ) ;
4664 }
4765
4866 break ;
4967 }
5068
51- IEnumerable < string > addedKeys = current . Properties ( ) . Select ( c => c . Name ) . Except ( old . Properties ( ) . Select ( c => c . Name ) ) ;
52- IEnumerable < string > removedKeys = old . Properties ( ) . Select ( c => c . Name ) . Except ( current . Properties ( ) . Select ( c => c . Name ) ) ;
53- IEnumerable < string > unchangedKeys = current . Properties ( ) . Where ( c => JToken . DeepEquals ( c . Value , old [ c . Name ] ) ) . Select ( c => c . Name ) ;
54- foreach ( string key in addedKeys )
69+ if ( oldObj != null && currentObj != null )
5570 {
56- FindDiff ( dict , new JObject ( ) , current [ key ] , Join ( prefix , key ) ) ;
71+ IEnumerable < string > addedKeys = currentObj . Properties ( ) . Select ( c => c . Name ) . Except ( oldObj . Properties ( ) . Select ( c => c . Name ) ) ;
72+ IEnumerable < string > removedKeys = oldObj . Properties ( ) . Select ( c => c . Name ) . Except ( currentObj . Properties ( ) . Select ( c => c . Name ) ) ;
73+ IEnumerable < string > unchangedKeys = currentObj . Properties ( ) . Where ( c => JToken . DeepEquals ( c . Value , oldObj [ c . Name ] ) ) . Select ( c => c . Name ) ;
74+ foreach ( string key in addedKeys )
75+ {
76+ FindDiff ( dict , JValue . CreateNull ( ) , currentObj [ key ] , Join ( prefix , key ) ) ;
77+ }
78+
79+ foreach ( string key in removedKeys )
80+ {
81+ FindDiff ( dict , oldObj [ key ] , JValue . CreateNull ( ) , Join ( prefix , key ) ) ;
82+ }
83+
84+ var potentiallyModifiedKeys = currentObj . Properties ( ) . Select ( c => c . Name ) . Except ( addedKeys ) . Except ( unchangedKeys ) ;
85+ foreach ( var key in potentiallyModifiedKeys )
86+ {
87+ FindDiff ( dict , oldObj [ key ] , currentObj [ key ] , Join ( prefix , key ) ) ;
88+ }
5789 }
5890
59- foreach ( string key in removedKeys )
91+ break ;
92+
93+ case JTokenType . Array :
94+ if ( oldArray != null )
6095 {
61- dict . Add ( Join ( prefix , key ) , null ) ;
62- }
96+ foreach ( var value in current . Children ( ) )
97+ {
98+ FindDiff (
99+ dict ,
100+ oldArray ? . Count - 1 >= index ? oldArray ? [ index ] : new JObject ( ) ,
101+ value ,
102+ $ "{ prefix } [{ index } ]") ;
103+
104+ index ++ ;
105+ }
63106
64- var potentiallyModifiedKeys = current . Properties ( ) . Select ( c => c . Name ) . Except ( addedKeys ) . Except ( unchangedKeys ) ;
65- foreach ( var key in potentiallyModifiedKeys )
107+ while ( index < oldArray ? . Count )
108+ {
109+ FindDiff ( dict , oldArray [ index ] , JValue . CreateNull ( ) , $ "{ prefix } [{ index } ]") ;
110+ index ++ ;
111+ }
112+ }
113+ else
66114 {
67- FindDiff ( dict , old [ key ] , current [ key ] , Join ( prefix , key ) ) ;
115+ if ( old ? . Type == JTokenType . Object )
116+ {
117+ FindDiff ( dict , old , JValue . CreateNull ( ) , prefix ) ;
118+ }
119+
120+ foreach ( JToken value in current . Children ( ) )
121+ {
122+ FindDiff ( dict , JValue . CreateNull ( ) , value , $ "{ prefix } [{ index } ]") ;
123+ index ++ ;
124+ }
68125 }
69126
70127 break ;
71128
72- case JTokenType . Array :
73- int index = 0 ;
74- foreach ( JToken value in Current . Children ( ) )
129+ case JTokenType . Null :
130+ if ( oldObj != null )
131+ {
132+ foreach ( string key in oldObj . Properties ( ) . Select ( c => c . Name ) )
133+ {
134+ FindDiff ( dict , oldObj [ key ] , JValue . CreateNull ( ) , Join ( prefix , key ) ) ;
135+ }
136+ }
137+ else if ( old ? . Type == JTokenType . Array )
138+ {
139+ for ( index = 0 ; index < oldArray ? . Count ; index ++ )
140+ {
141+ dict . Add ( $ "{ prefix } [{ index } ]", null ) ;
142+ }
143+ }
144+ else
75145 {
76- FindDiff ( dict , new JObject ( ) , value , $ "{ prefix } [{ index } ]") ;
77- index ++ ;
146+ dict . Add ( prefix , ( ( JValue ) current ) . Value ) ;
78147 }
79148
80149 break ;
81150
82151 default :
83- dict . Add ( prefix , ( ( JValue ) Current ) . Value ) ;
152+ dict . Add ( prefix , current == null ? null : ( ( JValue ) current ) . Value ) ;
84153 break ;
85154 }
86155 }
0 commit comments