Как избежать преобразования строки в число для двойных чисел и чисел, выраженных в научной нотации? - PullRequest
1 голос
/ 06 ноября 2019

Я получаю JSON полезную нагрузку, которая представляет собой набор пар ключ-значение. Значение может быть либо строкой, либо числом. Я должен разобрать JSON и сохранить пары ключ-значение в соответствующие varchar2 столбцы. Я должен сохранить входящий номер в точности так, как он был представлен во входных данных . Но для чисел, представленных как 1.1E4, 0.00000000000003 и аналогичных, вместо этого я получаю 11000.0, 3.0E-14.

Можно ли отключить / запретить преобразование чисел, чтобы вместо него было только строковое представление?

Я использую FasterXML Jackson реализацию. Кстати, фактического документа нет в наличии - все источники, которые я нашел, указывают на http://wiki.fasterxml.com/JacksonHome, который сейчас недоступен. Я нашел два похожих вопроса здесь Джексон JSON преобразует целые числа в строки Отключить автоматическое преобразование числа в строку в Джексоне , но оба требуют исключения, когда встречаются номера, что не является моим случаем. Я пробовал предлагаемые решения, но не смог изменить их, чтобы они соответствовали моей задаче.

Также нет ответа в https://github.com/FasterXML/jackson-databind/issues/796

В настоящее время у меня нет спецификации для входной строки, кроме key-пары значений. Так что просто пример:

I может получить что-то вроде:

{"a":"text", "b":"35", "c":{"d":"another"}, "e":["array",35], "f":1.1E4, "g":0.00000000000003}

Я хочу пары строк

"a" -> "text", "b" -> "35", "c" -> "{\"d\":\"another\"}", "e" -> "[\"array\",35]", "f" -> "1.1E4" 

Самый простой способ преобразования:

public void test() throws IOException {
    Map map = new ObjectMapper().readValue(
    "{\"a\":\"text\", \"b\":\"35\", \"c\":{\"d\":\"another\"}, \"e\":[\"array\",35], \"f\":1.1E4, \"g\":0.00000000000003}"
        , Map.class);
    System.out.println(map);
}

приводит к:

{a=text, b=35, c={d=another}, e=[array, 35], f=11000.0, g=3.0E-14}

Более точный способ:

public class JsonUtil2 {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    public static Map<String, String> parse(String json) throws IOException {
        ObjectNode objectNode = (ObjectNode) OBJECT_MAPPER.readTree(json);

        Map<String, String> result = new HashMap<>(objectNode.size());
        objectNode.fields().forEachRemaining(entry -> result.put(entry.getKey(), toJson(entry.getValue())));
        return result;
    }

    private static String toJson(JsonNode jsonNode) {
        if (jsonNode.isNumber()) {
            if (jsonNode instanceof DoubleNode || jsonNode instanceof FloatNode) {
                DecimalFormatSymbols dfs = new DecimalFormatSymbols();
                dfs.setDecimalSeparator('.');
                dfs.setMinusSign('-');
                DecimalFormat df = new DecimalFormat("#.#", dfs);
                df.setMaximumFractionDigits(32);
                df.setMaximumIntegerDigits(32);
                return df.format(jsonNode.doubleValue());
            } else {
                return jsonNode.asText();
            }
        } else if (jsonNode.isValueNode()) {
            return jsonNode.asText();
        } else {
            try {
                return OBJECT_MAPPER.writeValueAsString(jsonNode);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

, что приводит к:

{a=text, b=35, c={"d":"another"}, e=["array",35], f=11000, g=0.00000000000003}

Thisнамного лучше, но все равно различаются f=11000 вместо f=1.1E4.

1 Ответ

0 голосов
/ 08 ноября 2019

В вашем случае вы хотите рассматривать все как String, поэтому вам нужен специальный десериализатор, который читает JSON Object и JSON Array как String. Мы также можем заставить Jackson прочитать Map<String, String>, предоставив эту информацию, используя TypeFactory.

Предположим, что наша JSON полезная нагрузка выглядит следующим образом:

{
  "a": "text",
  "b": "35",
  "c": {
    "d": "another",
    "dd":3.44E3
  },
  "e": [
    "array",
    35,
    2.3E5
  ],
  "f": 1.1E4,
  "g": 0.00000000000003
}

Пример кода:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class JsonTreeApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        SimpleModule everythingIsStringModule = new SimpleModule();
        everythingIsStringModule.addDeserializer(String.class, new EverythingIsStringDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(everythingIsStringModule);

        MapType mapType = mapper.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, String.class);

        LinkedHashMap<String, String> map = mapper.readValue(jsonFile, mapType);
        map.forEach((k, v) -> System.out.println(k + " => " + v));
    }
}

class EverythingIsStringDeserializer extends StringDeserializer {

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.currentToken() == JsonToken.START_OBJECT) {
            return _deserializeFromObject(p, ctxt);
        }
        return super.deserialize(p, ctxt);
    }

    private String _deserializeFromObject(JsonParser p, DeserializationContext ctxt) throws IOException {
        MapType mapType = ctxt.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, String.class);
        JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(mapType);
        Map<String, String> map = (Map<String, String>) deserializer.deserialize(p, ctxt);

        return toString(map);
    }

    @Override
    protected String _deserializeFromArray(JsonParser p, DeserializationContext ctxt) throws IOException {
        CollectionType collectionType = ctxt.getTypeFactory().constructCollectionType(ArrayList.class, String.class);
        JsonDeserializer<Object> deserializer = ctxt.findRootValueDeserializer(collectionType);
        List<String> list = (List<String>) deserializer.deserialize(p, ctxt);

        return toString(list);
    }

    private String toString(Map<String, String> map) {
        StringBuilder builder = new StringBuilder(128);

        builder.append('{');
        boolean addComa = false;
        for (Map.Entry<String, String> entry : map.entrySet()) {
            if (addComa) {
                builder.append(',');
            }
            builder.append('"').append(entry.getKey())
                    .append("\":");
            appendValue(entry.getValue(), builder);
            addComa = true;
        }
        builder.append('}');

        return builder.toString();
    }

    private String toString(List<String> list) {
        StringBuilder builder = new StringBuilder(128);

        builder.append('[');
        boolean addComa = false;
        for (String item : list) {
            if (addComa) {
                builder.append(',');
            }
            appendValue(item, builder);
            addComa = true;
        }
        builder.append(']');

        return builder.toString();
    }

    private void appendValue(String value, StringBuilder builder) {
        if (value == null || value.isEmpty()) {
            builder.append("\"\"");
            return;
        }
        if (Character.isAlphabetic(value.charAt(0))) {
            builder.append('"').append(value).append('"');
        } else {
            builder.append(value);
        }
    }
}

Отпечатки:

a => text
b => 35
c => {d=another, dd=3.44E3}
e => [array, 35, 2.3E5]
f => 1.1E4
g => 0.00000000000003
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...