Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Matching JSON object with an instance

Assume I have following DTO:

class C {
    String a;
    String b;
}

And I have the JSON:

{
    "c" : {
        "a" : "aaa",
        "b" : "bbb"
    }
}

What I want to do is, accomplish following test:

C expected = new C("aaa","bbb");
mockMvc.perform(get("url"))
    .andExpect(jsonPath("$.c", is(expected)));

It fails. If I first serialize expected to JSON and then try to match, it again fails because it's a string. Is this possible?

like image 258
mtyurt Avatar asked Sep 28 '22 21:09

mtyurt


2 Answers

Always remember: There is no such thing as a "JSON object". JSON is a serialization format for objects. JSON is always a string. You can convert from object to JSON and back (and hence from object to string and back). But

{ "a": "b" }

is a JavaScript object, not JSON (even if it looks very similar).

This in fact is the answer to your question: When you serialize expected, you get JSON (the transport format, i.e. the string). This isn't what jsonPath() checks. jsonPath() validates against JavaScript types.

This blog post suggests that you need to check each field individually:

.andExpect(jsonPath("$.c.a", is(expected.a)))
.andExpect(jsonPath("$.c.b", is(expected.b)));

which is tedious. What you need is

a) to configure your JSON framework to use a mapping system that sorts keys and

b) you need to figure out what type jsonPath("$.c", ...) returns - it's probably the type which your JSON framework uses to represent generic JavaScript objects.

The check then looks like this:

C c = new C("aaa","bbb");
String serialized = JSON.serialize(c); // to string
JSObject expected = JSON.parse(serialized); // to generic JavaScript object
mockMvc.perform(get("url"))
    .andExpect(jsonPath("$.c", is(expected)));

Note that this only works if JSObject has a proper implementation for equals().

like image 197
Aaron Digulla Avatar answered Oct 07 '22 16:10

Aaron Digulla


If you can afford to modify your "C" class to add it an "equals" operator and to modify slightly your JSON file, I would suggest you to transform your JSON string into an instance of "C". This can be done with a good JSON-ifier (Jackson or GSON). Then you just have to compare the 2 instances. Some examples with GSON:

class C {
    String a;
    String b;
    public boolean equals(C obj) { return a.equals(obj.a) && b.equals(obj.b); }
}
// Your JSON file should look like that
{
    "a" : "aaa",
    "b" : "bbb"
}
// So the test is simple
C expected = new C("aaa","bbb");
C json = gson.fromJson(jsonString, C.class); 
if (expected.equals(json)) {
    // Do whatever you want here
}

If you cannot afford to change the JSON file, just create another class to contains your main class, like this:

class Wrapper {
    C c;
}
Wrapper jsonW = gson.fromJson(jsonString, Wrapper.class);
C json = jsonW.c;
...

If you cannot afford the addition of the equals operator, I suggest to create JSON string based on the 2 "C" instance objects and compare the strings. Your jsonString becomes a real "C" object (json) before ending into a new string (jsonStr).

String expectedStr = gson.toJson(expected);
String jsonStr = gson.toJSON(json);
if (expectedStr.equals(jsonStr)) {
    // Do whatever you want here
}
like image 41
TrapII Avatar answered Oct 07 '22 17:10

TrapII