1919
2020package com .github .fge .jsonpatch .diff2 ;
2121
22- import com .fasterxml .jackson .core .type .TypeReference ;
2322import com .fasterxml .jackson .databind .JsonNode ;
2423import com .fasterxml .jackson .databind .ObjectMapper ;
2524import com .fasterxml .jackson .databind .node .ArrayNode ;
26- import com .fasterxml .jackson .databind .node .JsonNodeFactory ;
2725import com .fasterxml .jackson .databind .node .ObjectNode ;
2826import com .github .fge .jackson .JacksonUtils ;
2927import com .github .fge .jackson .JsonNumEquals ;
3028import com .github .fge .jackson .NodeType ;
3129import com .github .fge .jackson .jsonpointer .JsonPointer ;
3230import com .github .fge .jsonpatch .JsonPatch ;
33- import com .github .fge .jsonpatch .MoveOperation ;
3431import com .google .common .annotations .VisibleForTesting ;
3532import com .google .common .base .Equivalence ;
36- import com .google .common .collect .ImmutableMap ;
3733import com .google .common .collect .Maps ;
3834import com .google .common .collect .Sets ;
3935
4339import java .util .Map ;
4440import java .util .Set ;
4541
42+ /**
43+ * JSON "diff" implementation
44+ *
45+ * <p>This class generates a JSON Patch (as in, an RFC 6902 JSON Patch) given
46+ * two JSON values as inputs. The patch can be obtained directly as a {@link
47+ * JsonPatch} or as a {@link JsonNode}.</p>
48+ *
49+ * <p>Note: there is <b>no guarantee</b> about the usability of the generated
50+ * patch for any other source/target combination than the one used to generate
51+ * the patch.</p>
52+ *
53+ * @since 1.2
54+ */
4655@ ParametersAreNonnullByDefault
4756public final class JsonDiff
4857{
@@ -55,42 +64,60 @@ private JsonDiff()
5564 {
5665 }
5766
58- public static JsonPatch asJsonPatch (final JsonNode first ,
59- final JsonNode second )
67+ /**
68+ * Generate a JSON patch for transforming the source node into the target
69+ * node
70+ *
71+ * @param source the node to be patched
72+ * @param target the expected result after applying the patch
73+ * @return the patch as a {@link JsonPatch}
74+ *
75+ * @since 1.9
76+ */
77+ public static JsonPatch asJsonPatch (final JsonNode source ,
78+ final JsonNode target )
6079 {
6180 final Map <JsonPointer , JsonNode > unchanged
62- = getUnchangedValues (first , second );
81+ = getUnchangedValues (source , target );
6382 final DiffProcessor processor = new DiffProcessor (unchanged );
6483
65- generateDiffs (processor , JsonPointer .empty (), first , second );
84+ generateDiffs (processor , JsonPointer .empty (), source , target );
6685 return processor .getPatch ();
6786 }
6887
69- public static JsonNode asJson (final JsonNode first , final JsonNode second )
88+ /**
89+ * Generate a JSON patch for transforming the source node into the target
90+ * node
91+ *
92+ * @param source the node to be patched
93+ * @param target the expected result after applying the patch
94+ * @return the patch as a {@link JsonNode}
95+ */
96+ public static JsonNode asJson (final JsonNode source , final JsonNode target )
7097 {
7198 final String s ;
7299 try {
73- s = MAPPER .writeValueAsString (asJsonPatch (first , second ));
100+ s = MAPPER .writeValueAsString (asJsonPatch (source , target ));
74101 return MAPPER .readTree (s );
75102 } catch (IOException e ) {
76103 throw new RuntimeException ("cannot generate JSON diff" , e );
77104 }
78105 }
79106
80107 private static void generateDiffs (final DiffProcessor processor ,
81- final JsonPointer pointer , final JsonNode first , final JsonNode second )
108+ final JsonPointer pointer , final JsonNode source , final JsonNode target )
82109 {
83- if (EQUIVALENCE .equivalent (first , second ))
110+ if (EQUIVALENCE .equivalent (source , target ))
84111 return ;
85112
86- final NodeType firstType = NodeType .getNodeType (first );
87- final NodeType secondType = NodeType .getNodeType (second );
113+ final NodeType firstType = NodeType .getNodeType (source );
114+ final NodeType secondType = NodeType .getNodeType (target );
88115
89116 /*
90117 * Node types differ: generate a replacement operation.
91118 */
92119 if (firstType != secondType ) {
93- processor .valueReplaced (pointer , first , second );
120+ processor .valueReplaced (pointer , source , target );
94121 return ;
95122 }
96123
@@ -100,8 +127,8 @@ private static void generateDiffs(final DiffProcessor processor,
100127 *
101128 * If this is not a container, generate a replace operation.
102129 */
103- if (!first .isContainerNode ()) {
104- processor .valueReplaced (pointer , first , second );
130+ if (!source .isContainerNode ()) {
131+ processor .valueReplaced (pointer , source , target );
105132 return ;
106133 }
107134
@@ -110,64 +137,64 @@ private static void generateDiffs(final DiffProcessor processor,
110137 * delegate.
111138 */
112139 if (firstType == NodeType .OBJECT )
113- generateObjectDiffs (processor , pointer , (ObjectNode ) first ,
114- (ObjectNode ) second );
140+ generateObjectDiffs (processor , pointer , (ObjectNode ) source ,
141+ (ObjectNode ) target );
115142 else // array
116- generateArrayDiffs (processor , pointer , (ArrayNode ) first ,
117- (ArrayNode ) second );
143+ generateArrayDiffs (processor , pointer , (ArrayNode ) source ,
144+ (ArrayNode ) target );
118145 }
119146
120147 private static void generateObjectDiffs (final DiffProcessor processor ,
121- final JsonPointer pointer , final ObjectNode first ,
122- final ObjectNode second )
148+ final JsonPointer pointer , final ObjectNode source ,
149+ final ObjectNode target )
123150 {
124151 final Set <String > firstFields
125- = Sets .newTreeSet (Sets .newHashSet (first .fieldNames ()));
152+ = Sets .newTreeSet (Sets .newHashSet (source .fieldNames ()));
126153 final Set <String > secondFields
127- = Sets .newTreeSet (Sets .newHashSet (second .fieldNames ()));
154+ = Sets .newTreeSet (Sets .newHashSet (target .fieldNames ()));
128155
129156 for (final String field : Sets .difference (firstFields , secondFields ))
130- processor .valueRemoved (pointer .append (field ), first .get (field ));
157+ processor .valueRemoved (pointer .append (field ), source .get (field ));
131158
132159 for (final String field : Sets .difference (secondFields , firstFields ))
133- processor .valueAdded (pointer .append (field ), second .get (field ));
160+ processor .valueAdded (pointer .append (field ), target .get (field ));
134161
135162 for (final String field : Sets .intersection (firstFields , secondFields ))
136- generateDiffs (processor , pointer .append (field ), first .get (field ),
137- second .get (field ));
163+ generateDiffs (processor , pointer .append (field ), source .get (field ),
164+ target .get (field ));
138165 }
139166
140167 private static void generateArrayDiffs (final DiffProcessor processor ,
141- final JsonPointer pointer , final ArrayNode first ,
142- final ArrayNode second )
168+ final JsonPointer pointer , final ArrayNode source ,
169+ final ArrayNode target )
143170 {
144- final int firstSize = first .size ();
145- final int secondSize = second .size ();
171+ final int firstSize = source .size ();
172+ final int secondSize = target .size ();
146173 final int size = Math .min (firstSize , secondSize );
147174
148175 for (int index = 0 ; index < size ; index ++)
149- generateDiffs (processor , pointer .append (index ), first .get (index ),
150- second .get (index ));
176+ generateDiffs (processor , pointer .append (index ), source .get (index ),
177+ target .get (index ));
151178
152179 /*
153180 * Source array is larger; in this case, elements are removed from the
154181 * target; the index of removal is always the original arrays's length.
155182 */
156183 for (int index = size ; index < firstSize ; index ++)
157- processor .valueRemoved (pointer .append (size ), first .get (index ));
184+ processor .valueRemoved (pointer .append (size ), source .get (index ));
158185
159186 // Deal with the destination array being larger...
160187 for (int index = size ; index < secondSize ; index ++)
161- processor .valueAdded (pointer .append ("-" ), second .get (index ));
188+ processor .valueAdded (pointer .append ("-" ), target .get (index ));
162189 }
163190
164191
165192 @ VisibleForTesting
166- static Map <JsonPointer , JsonNode > getUnchangedValues (final JsonNode first ,
167- final JsonNode second )
193+ static Map <JsonPointer , JsonNode > getUnchangedValues (final JsonNode source ,
194+ final JsonNode target )
168195 {
169196 final Map <JsonPointer , JsonNode > ret = Maps .newHashMap ();
170- computeUnchanged (ret , JsonPointer .empty (), first , second );
197+ computeUnchanged (ret , JsonPointer .empty (), source , target );
171198 return ret ;
172199 }
173200
@@ -199,57 +226,29 @@ private static void computeUnchanged(final Map<JsonPointer, JsonNode> ret,
199226 }
200227
201228 private static void computeObject (final Map <JsonPointer , JsonNode > ret ,
202- final JsonPointer pointer , final JsonNode first , final JsonNode second )
229+ final JsonPointer pointer , final JsonNode source ,
230+ final JsonNode target )
203231 {
204- final Iterator <String > firstFields = first .fieldNames ();
232+ final Iterator <String > firstFields = source .fieldNames ();
205233
206234 String name ;
207235
208236 while (firstFields .hasNext ()) {
209237 name = firstFields .next ();
210- if (!second .has (name ))
238+ if (!target .has (name ))
211239 continue ;
212- computeUnchanged (ret , pointer .append (name ), first .get (name ),
213- second .get (name ));
240+ computeUnchanged (ret , pointer .append (name ), source .get (name ),
241+ target .get (name ));
214242 }
215243 }
216244
217245 private static void computeArray (final Map <JsonPointer , JsonNode > ret ,
218- final JsonPointer pointer , final JsonNode first , final JsonNode second )
246+ final JsonPointer pointer , final JsonNode source , final JsonNode target )
219247 {
220- final int size = Math .min (first .size (), second .size ());
248+ final int size = Math .min (source .size (), target .size ());
221249
222250 for (int i = 0 ; i < size ; i ++)
223- computeUnchanged (ret , pointer .append (i ), first .get (i ),
224- second .get (i ));
225- }
226-
227- public static void main (final String ... args )
228- throws IOException
229- {
230- final ImmutableMap .Builder <JsonPointer , JsonNode > builder
231- = ImmutableMap .builder ();
232-
233- builder .put (JsonPointer .of ("a" ), JsonNodeFactory .instance .arrayNode ());
234-
235- final ImmutableMap <JsonPointer , JsonNode > map = builder .build ();
236-
237- final ObjectMapper mapper = JacksonUtils .newMapper ();
238-
239- final JsonNode node = mapper .convertValue (map , JsonNode .class );
240- System .out .println (node );
241-
242- final TypeReference <Map <JsonPointer , JsonNode >> typeRef
243- = new TypeReference <Map <JsonPointer , JsonNode >>()
244- {
245- };
246-
247- final Map <JsonPointer , JsonNode > map2
248- = mapper .readValue (node .traverse (), typeRef );
249-
250- System .out .println (map2 );
251-
252- System .out .println (mapper .writeValueAsString (new MoveOperation
253- (JsonPointer .of ("a" ), JsonPointer .of ("b" ))));
251+ computeUnchanged (ret , pointer .append (i ), source .get (i ),
252+ target .get (i ));
254253 }
255254}
0 commit comments