Отключить преобразование скаляров в строки при десериализации с Джексоном - PullRequest
3 голосов
/ 24 апреля 2019

Я хочу идентифицировать числовые значения, вставленные без кавычек (в виде строк) в JSON, отправленные через тело запроса POST:

Например, это будет неправильный формат JSON, так как поле age не содержит кавычек:

{
  "Student":{
    "Name": "John",
    "Age":  12
  }
}

Правильный формат JSON будет:

{
  "Student":{ 
    "Name": "John",
    "Age":  "12"
  }
}

В моем коде я определил тип данных поля age как String, поэтому "12" должен быть правильным вводом. Однако сообщение об ошибке не выдается, даже если используется 12.

Кажется, Джексон автоматически конвертирует числовые значения в строки. Как я могу определить числовые значения и вернуть сообщение?

Это то, что я до сих пор пытался определить эти числовые значения:

public List<Student> getMultiple(StudentDTO Student) {
    if(Student.getAge().getClass()==String.class) {
        System.out.println("Age entered correctly as String");
    } else{
        System.out.println("Please insert age value inside inverted commas");
    }
}

Однако, это не выводит "Please insert age value inside inverted commas" на консоль, когда возраст вставляется без кавычек.

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Если вы используете загрузку Spring, по умолчанию она использует Jackson для анализа JSON. В Джексоне нет опции конфигурации для отключения этой функции, как упоминалось в этой проблеме . Решение состоит в том, чтобы зарегистрировать пользовательский JsonDeserializer, который выдает исключение, как только он встречает любой другой токен, кроме JsonToken.VALUE_STRING

public class StringOnlyDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        if (!JsonToken.VALUE_STRING.equals(jsonParser.getCurrentToken())) {
            throw deserializationContext.wrongTokenException(jsonParser, String.class, JsonToken.VALUE_STRING, "No type conversion is allowed, string expected");
        } else {
            return jsonParser.getValueAsString();
        }
    }
}

Если вы хотите применить это только к определенным классам или полям, вы можете пометить их аннотацией @JsonDeserialize. Например:

public class Student {
    private String name;
    @JsonDeserialize(using = StringOnlyDeserializer.class)
    private String age;

    // TODO: Getters + Setters
}

Кроме того, вы можете зарегистрировать пользовательский модуль Джексона, зарегистрировав компонент SimpleModule, который автоматически десериализует все строки, используя StringOnlyDeserializer. Например:

@Bean
public Module customModule() {
    SimpleModule customModule = new SimpleModule();
    customModule.addDeserializer(String.class, new StringOnlyDeserializer());
    return customModule;
}

Это похоже на то, что предложил Евгений .

Если вы запустите приложение сейчас и укажете недопустимый возраст, такой как 12, 12.3 или [12], оно выдаст исключение с сообщением типа:

JSON parse error: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string\n at [Source: (PushbackInputStream); line: 3, column: 9] (through reference chain: com.example.xyz.Student[\"age\"])
1 голос
/ 24 апреля 2019

По умолчанию Джексон преобразует скалярные значения в String, если поле назначения имеет тип String.Идея состоит в том, чтобы создать пользовательский десериализатор для String типа и закомментировать часть преобразования:

package jackson.deserializer;

import java.io.IOException;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;


public class CustomStringDeserializer extends StringDeserializer 
{

    public final static CustomStringDeserializer instance = new CustomStringDeserializer();

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.hasToken(JsonToken.VALUE_STRING)) {
            return p.getText();
        }
        JsonToken t = p.getCurrentToken();
        // [databind#381]
        if (t == JsonToken.START_ARRAY) {
            return _deserializeFromArray(p, ctxt);
        }
        // need to gracefully handle byte[] data, as base64
        if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
            Object ob = p.getEmbeddedObject();
            if (ob == null) {
                return null;
            }
            if (ob instanceof byte[]) {
                return ctxt.getBase64Variant().encode((byte[]) ob, false);
            }
            // otherwise, try conversion using toString()...
            return ob.toString();
        }
        // allow coercions for other scalar types
        // 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
        //   "real" scalar
        /*if (t.isScalarValue()) {
            String text = p.getValueAsString();
            if (text != null) {
                return text;
            }
        }*/
        return (String) ctxt.handleUnexpectedToken(_valueClass, p);
    }

}

Теперь зарегистрируйте этот десериализатор:

@Bean
public Module customStringDeserializer() {
    SimpleModule module = new SimpleModule();
    module.addDeserializer(String.class, CustomStringDeserializer.instance);
    return module;
}

Когда передается целое число и строкаожидается, вот ошибка:

{"timestamp": "2019-04-24T15: 15: 58.968 + 0000", "status": 400, "error": "Bad Request", "message": "Ошибка синтаксического анализа JSON: Невозможно десериализовать экземпляр java.lang.String из токена VALUE_NUMBER_INT; вложенным исключением является com.fasterxml.jackson.databind.exc.MismatchedInputException: Невозможно десериализовать экземпляр java.lang.String из токена VALUE_NUMBER_INT \ nat [Source: (PushbackInputStream); строка: 3, столбец: 13] (через цепочку ссылок: org.hello.model.Student [\ "age \"]) "," path ":" / hello / echo "}

...