Home > OS >  Jackson array deserialize to java: MismatchedInputException: Unexpected end-of-input when trying rea
Jackson array deserialize to java: MismatchedInputException: Unexpected end-of-input when trying rea

Time:12-28

Description

I'm trying to parse json array string to java collection, and I've checked the format of the array string. There is my test case:

List<PropertySchema> schemas = new ArrayList<>();
schemas.add(new PropertySchema("test1", "test1", new BoolDataType(), null, null));
schemas.add(new PropertySchema("test2", "test2", new LongDataType(), null, null));
schemas.add(new PropertySchema("test3","test3",new DoubleDataType(),null,null));
schemas.add(new PropertySchema("test4","test4",new TextDataType(),null,null));

final String js = new ObjectMapper().writeValueAsString(schemas);
System.out.println(js);

schemas = new ObjectMapper().readValue(js, new TypeReference<List<PropertySchema>>() {
        });

System.out.println(schemas);

writeValueAsString is succeed but readValue throw exception:

Exception

com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected end-of-input when trying read value of type `com.cainiao.iot.digital.map.bean.PropertySchema`
 at [Source: (String)"[{"code":"test1","name":"test1","dataType":{"type":"Boolean"},"description":null,"ui":null,"defaultValue":null,"editable":true},{"code":"test2","name":"test2","dataType":{"type":"Long","unit":null},"description":null,"ui":null,"defaultValue":null,"editable":true},{"code":"test3","name":"test3","dataType":{"type":"Double","unit":null},"description":null,"ui":null,"defaultValue":null,"editable":true},{"code":"test4","name":"test4","dataType":{"type":"String"},"description":null,"ui":null,"defaultV"[truncated 28 chars]; line: 1, column: 528] (through reference chain: java.util.ArrayList[1])

    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1462)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:223)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:186)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer._deserializeFromArray(CollectionDeserializer.java:355)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:244)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:28)
    at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:322)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3612)
    at com.cainiao.JacksonTest.listDeserialize(JacksonTest.java:89)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

I can't figure out why, there is the java bean class:

Code

@Data
@Accessors(chain = true)
@JsonIgnoreProperties(ignoreUnknown = true)
public class PropertySchema implements Serializable {
    private static final long serialVersionUID = 4534224437154640700L;
    private String code;
    private String name;
    @JsonDeserialize(using = TypeDeserializer.class)
    private DataType dataType;
    private String description;
    private String ui;
    private Object defaultValue = "";
    private boolean editable = true;

    public PropertySchema() {
    }


    public PropertySchema(String code, String name, DataType dataType, Object defaultValue, String description) {
        this.code = code;
        this.name = name;
        this.dataType = dataType;
        this.description = description;
        this.defaultValue = defaultValue;
    }
}

public class TypeDeserializer extends JsonDeserializer<DataType> {
    @Override
    public DataType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        String type = null;
        String unit = null;
        while (!jsonParser.isClosed()) {
            final JsonToken jsonToken = jsonParser.nextToken();
            if (JsonToken.FIELD_NAME == jsonToken) {
                final String fieldName = jsonParser.currentName();
                jsonParser.nextToken();
                switch (fieldName) {
                    case "type":
                        type = jsonParser.getValueAsString();
                        break;
                    case "unit":
                        unit = jsonParser.getValueAsString();
                        break;
                    default:
                }
            }
        }
        if (type == null) {
            return null;
        }
        DataType dataType;
        switch (type) {
            case "Double": {
                dataType = new DoubleDataType();
                if (unit != null) {
                    ((DoubleDataType) dataType).setUnit(unit);
                }
                break;
            }
            case "Boolean": {
                dataType = new BoolDataType();
                break;
            }
            case "Long": {
                dataType = new LongDataType();
                if (unit != null) {
                    ((LongDataType) dataType).setUnit(unit);
                }
                break;
            }
            case "String": {
                dataType = new TextDataType();
                break;
            }
            default:
                return null;
        }
        return dataType;
    }
}

What's the correct way to deserialize array string to java collection ?

CodePudding user response:

The problem isn't really about deserializing a collection. I think the while loop here in the TypeDeserializer isn't working properly. It seems it exhausts the entire objects the first time it is used. Instead of looping the object yourself, this is a better way of deserializing the DataType:

JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser);
String type = jsonNode.get("type").asText(); 
String unit = jsonNode.get("unit").asText(); // I'm assuming the type and unit fields are Strings.

The rest of the deserializer can largely stay the same. Here is a good tutorial about using custom deserialization in Jackson

  • Related