Search code examples
javajsongsonjsonreader

Using JsonReader to read dynamic json files


As part of an API I am creating, I have allowed the specification of configurations (which can be any storage format, just one implementation is with Json). As part of that, my code will have no knowledge of what the configuration truly holds. I am using the Gson library for reading a json-implemented config, however I have hit a snag in how numbers are handled with it. My current code uses a recursion to read the inner objects, and contains the following:

private JsonConfigurationSection readObject(JsonReader in) throws IOException {
    JsonConfigurationSection section = new JsonConfigurationSection();
    in.beginObject();
    while (in.peek() != JsonToken.END_OBJECT) {
        String name = in.nextName();
        switch (in.peek()) {
            case BEGIN_OBJECT: {
                section._internal.put(name, readObject(in));
            }
            break;
            case BEGIN_ARRAY: {
                in.beginArray();
                List<String> array = new LinkedList<>();
                while (in.peek() != JsonToken.END_ARRAY) {
                    array.add(in.nextString());
                }
                in.endArray();
                section._internal.put(name, array);
            }
            break;
            case BOOLEAN: {
                boolean next = in.nextBoolean();
                section._internal.put(name, next);
            }
            break;
            case NUMBER: {
                //read the next number, whether long, int, or double    
                section._internal.put(name, next);
            }
            break;
            case STRING: {
                String next = in.nextString();
                section._internal.put(name, next);
            }
            break;
        }
    }
    in.endObject();
}

The JsonConfigurationSection class is simply a wrapper around a Map:

class JsonConfigurationSection implements ConfigurationSection {

    final Map<String, Object> _internal = new TreeMap<>();

    //methods being inherited, just getters for data from the map
}

An example of a configuration might be

{
  "server": {
    "ip": "127.0.0.1",
    "port": 3306
  }
  "someval": 33.4
}

The issue that has arisen is that the JsonReader only provides a next token for "Number", but then specific getters that are for longs, doubles, and ints.

What would be the best way to get what that number is without any loss of data and using the "best" storage for it? (I am willing to drop longs, but would rather see if I could keep them for consistency)


Solution

  • After playing around a little, it seems the best way is to just use the nextString and use the BigDecimal as was suggested by a comment:

     String line = in.nextString();
     BigDecimal decimal = new BigDecimal(line);
     try {
         section._internal.put(name, decimal.intValueExact());
     } catch (ArithmeticException e) {
         try {
             section._internal.put(name, decimal.longValueExact());
         } catch (ArithmeticException ex) {
             section._internal.put(name, decimal.doubleValue());
         }
    }
    

    This really is just checking to see if the value can fit into a int (which is the most limited of the 3 types), then a long, and if that would fail, then just keep as a double.