Использование JsonReader для чтения динамических файлов json

Как часть API, который я создаю, я разрешил спецификацию конфигураций (это может быть любой формат хранения, только одна реализация — с Json). Как часть этого, мой код не будет знать, что на самом деле содержит конфигурация. Я использую библиотеку Gson для чтения конфигурации, реализованной в json, однако я столкнулся с проблемой в том, как с ней обрабатываются числа. Мой текущий код использует рекурсию для чтения внутренних объектов и содержит следующее:

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();
}

Класс JsonConfigurationSection — это просто оболочка для Map:

class JsonConfigurationSection implements ConfigurationSection {

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

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

Примером конфигурации может быть

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

Возникшая проблема заключается в том, что JsonReader предоставляет только следующий токен для «Числа», но затем определенные геттеры для длинных, двойных и целых чисел.

Каков наилучший способ получить это число без потери данных и с использованием для него «лучшего» хранилища? (Я готов отказаться от длинных позиций, но предпочитаю посмотреть, смогу ли я сохранить их для последовательности)


person Joshua Taylor    schedule 03.03.2015    source источник
comment
Я не очень хорошо знаю эту библиотеку, но вы можете вызвать nextString, чтобы получить число, а затем передать это значение в новый BigDecimal и пусть BigDecimal определит, является ли это двойным или целым числом.   -  person Lucas Ross    schedule 03.03.2015
comment
Я думал о преобразовании, но это кажется мне немного уродливым, и я надеялся, что есть другие способы, которые будут работать более естественно.   -  person Joshua Taylor    schedule 03.03.2015


Ответы (2)


Извините, что поместил это как ответ. Недостаточно репутации, чтобы добавить комментарий.

Так. Просто как идея. Вы можете расширить java.lang.Number и сохранить значение «как есть» в строке.

person shapkin    schedule 03.03.2015
comment
Я могу просто сохранить строку и самостоятельно выполнять преобразования при вызовах, если захочу пойти по этому пути. Продление номера просто напрашивается на неприятности. - person Joshua Taylor; 03.03.2015
comment
Согласно стандарту json, число — это просто последовательность цифр. Поэтому, если вы ожидаете получить целое число, сохраните его как целое число. Я думаю, это хорошая идея. Также у вас будет еще один способ проверить, не является ли номер порта числом с плавающей запятой. - person shapkin; 04.03.2015
comment
Так в целом. если число имеет '.' держите его двойным. Еще пока. - person shapkin; 04.03.2015
comment
Я ничего особенного не жду. Это был пример конфигурации, просто чтобы показать, чего я могу ожидать от чего угодно. Смысл кода, который я делал, в том, чтобы он был динамическим, что, если я чего-то ожидаю, не сделает его динамическим. - person Joshua Taylor; 04.03.2015

Немного поиграв, кажется, что лучший способ - просто использовать nextString и использовать BigDecimal, как было предложено в комментарии:

 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());
     }
}

На самом деле это просто проверка того, может ли значение поместиться в int (который является наиболее ограниченным из трех типов), затем в long, и если это не удастся, то просто сохраните как double.

person Joshua Taylor    schedule 04.03.2015