1

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)

Joshua Taylor
  • 75
  • 1
  • 8
  • I don't know this library really well, but you could call `nextString` to get the number, then pass that value to a new [`BigDecimal`](http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html) and let `BigDecimal` figure out whether it's a double or an integer. – Lucas Ross Mar 03 '15 at 17:06
  • I had thought about the conversion, but that seems a bit ugly to me, and was kinda hoping there was other ways that would work more "natively" – Joshua Taylor Mar 03 '15 at 17:08

2 Answers2

0

Sorry for putting it as answer. Have no enough reputation to add comment.

So. Just as idea. You can extend java.lang.Number And keep value "as is" in String.

shapkin
  • 358
  • 3
  • 10
  • I can just store a string and do conversions myself on calls if I wanted to go that route. Extending Number is just asking for trouble. – Joshua Taylor Mar 03 '15 at 17:59
  • According to json standard, number just a sequence of digits. So if you expecting to get integer keep it as Integer. I think it is a good idea. Also you will have one more way to check if port number is not a float. – shapkin Mar 04 '15 at 13:08
  • So in general. if number have '.' keep it as double. Else as long. – shapkin Mar 04 '15 at 13:13
  • I am not expecting much of anything. That was an **example** of a config, just to show what I could expect from anything. The point of the code I was making to have it be dynamic, which if I expect something, is not going to make it dynamic. – Joshua Taylor Mar 04 '15 at 15:59
0

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.

Joshua Taylor
  • 75
  • 1
  • 8