2

I'm currently writing a jsonrpc-client in java. A request is represented by an JSONObject like this:

{'method': 'do.stuff', 'params': ['asdf', 3, {'foo': 'bar'}]}

Now I want to implement a low-level request cache and for this I need a way to create a hash from a JSONObject that will always return the same value. Using the toString() is not an option since JSONObjects are unordered: the following example would do exactly the same as my first example even though the string representation is different:

{'params': ['asdf', 3, {'foo': 'bar'}], 'method': 'do.stuff'}

Whats the best solution to this problem? The jsonobject may be nested to any arbitrary depth of course. I'm fairly new to Java and thankful for any suggestion.

oli
  • 173
  • 2
  • 2
  • 7

4 Answers4

10

You want to have a look at Jackson: read your input as a JsonNode, which implements .equals() and .hashCode() correctly (that is, sticking to the contract established by java.lang.Object), for all JSON values (numbers, booleans, strings, objects recursively, arrays recursively, nulls).

fge
  • 119,121
  • 33
  • 254
  • 329
  • @Codo oh it does, however `JsonNode` is much more powerful than its GSON equivalent, at least navigation-wise. – fge Jan 02 '13 at 22:52
2

I agree with @fge's solution. This is working for me across multiple variations of a JSON structure -

    int hash = 0;
    ObjectMapper mapper = new ObjectMapper();
    JsonFactory factory = mapper.getFactory();
    try {
        JsonNode jsonNode = mapper.readTree(factory.createParser(jsonString));
        hash = jsonNode.hashCode();
    } catch (IOException e) {
        LOG.error("Error generating hash for jsonString: {}", jsonString);
        e.printStackTrace();
    }


Example permutations - 
 "{ k1: v1, k2:v2, k3: v3}"
 "{ k3: v3, k2:v2, k1: v1}"
 "{ k3: v3, k1:v1, k2: v2}"

All 3 produced same hashcode everytime. In practice however my json also had ArrayList and Map within itself and that worked perfectly well too.

quealegriamasalegre
  • 2,887
  • 1
  • 13
  • 35
user2756335
  • 149
  • 1
  • 2
0

I suggest calling the hashCode method on the JSONObject as it will not depend on the ordering of the elements or the string representation.

kldavis4
  • 2,177
  • 1
  • 22
  • 33
-4

You can just do .toString().hashCode()

Even though JSON is unordered, I'm thinking the strings would be generated the same way each time. Even on the off chance they were not generated the same, You would just have two objects in your cache, which probably wouldn't be the end of the world.

aglassman
  • 2,643
  • 1
  • 17
  • 30
  • 1
    The documentation doesn't guarantee that the string will always be the same for the same mappings. And the question states a requirement that the hash will always be the same value for the same mappings. – nasch Apr 01 '15 at 16:22