简体   繁体   中英

How to deserialize a json array followed by a normal property into a pojo in jackson. The array alone works

The difficulty is, that the json labels are partly dynamic in the rest response of the Kraken api. At first I introduce a working case. I connect the Kraken trading api to fetch currencies as assets and got the following result in json.

{
    "error": [],
    "result": {
        "AAVE": {
            "aclass": "currency",
            "altname": "AAVE",
            "decimals": 10,
            "display_decimals": 5
        },
        "ZUSD": {
            "aclass": "currency",
            "altname": "USD",
            "decimals": 4,
            "display_decimals": 2
        }
    }
}

AAVA and ZUSD in this example are dynamic labels. I use the embedded Jackson to parse it in the OpenFeign framework. The result part are covered with the following generic class:

public class Response<T> {
    
    private List<String> error = new ArrayList<>();
    private T result;
    
    // getter and setters
}

And as root class for the assets, the dynamic labels AAVA and ZUSD are handled by a Map:

public class AssetInfoResponse extends 
              Response<Map<String, AssetInfo>> {
}

The pojo AssetInfo:

public class AssetInfo implements Serializable{

    @JsonProperty("altname")
    private String alternateName;

    @JsonProperty("aclass")
    private String assetClass;

    @JsonProperty("decimals")
    private Byte decimals;

    @JsonProperty("display_decimals")
    private Byte displayDecimals;

    // getters, setters ...
}

The above case works perfectly, also the solution with the dynamic labels.

Here is the response with the ohlc data, that looks similar and I have no Idea to solve the deserialization problem in the next case:

{
    "error": [],
    "result": {
        "XXBTZEUR": [
            [
                1613212500,
                "39000.1",
                "39010.1",
                "38972.3",
                "38994.1",
                "38998.1",
                "3.23811638",
                70
            ],
            [
                1613212560,
                "38994.3",
                "39014.5",
                "38994.3",
                "39014.5",
                "38997.3",
                "0.95105956",
                11
            ]
        ],
        "last": 1613212500
    }
}

The cause of the problem is the "last": 1613212500 property line. When I remove this line, the response can be parsed without problems. I try to solve it with the following classes, Response is the upper described class.

public class OhlcLastResponse<T> extends Response<T> {

    private Long last;
   
    // getters and setters

}

The next class extends the prevourious class and is the root class for the objectmapper:

public class OhlcResponse
      extends OhlcLastResponse<Map<String, List<Candelstick>>> {
}

And the pojo that holds the candlestick data:

@JsonFormat(shape = JsonFormat.Shape.ARRAY)
@JsonPropertyOrder({ "time", "open", "high", "low",
                     "close", "vwap", "volume", "count" })
public class Candelstick implements Serializable {
    
    private Integer time;

    private BigDecimal open;

    private BigDecimal high;

    private BigDecimal low;

    private BigDecimal close;

    private BigDecimal vwap;

    private BigDecimal volume;

    private Integer count;

    // getters and setters ...
}

and here is the error:

                "38997.3",
                "0.95105956",
                11
            ]
        ],
        "last": 1613212500
    }
}
"; line: 26, column: 11] (through reference chain: OhlcResponse["result"]->java.util.LinkedHashMap["last"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)

Jackson tries to put the last property into the map, but the map was finished by ], . Line 26 is the line with the last label in the json file.

Is there a possibility to parse this json? I think it must be possible, because the array is closed by the square bracket.

I hosted the rest client on github . To reproduce the error just clone it and run mvn test .

The solution is a deserializer, because the type handling is very difficult in this case. The deserializer decide between the two cases, array or the single last value, and call in the case of an array the deserializer for the CandleStick Class:

public class OhlcDeserializer extends JsonDeserializer<OhclPayload> {

    @Override
    public OhclPayload deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        List<CandleStick> candleSticks = new ArrayList<CandleStick>();

        Long last = null;

        ObjectCodec objectCodec = p.getCodec();
        JsonNode jsonNode = objectCodec.readTree(p);

        Iterator<Entry<String, JsonNode>> payloadIterator = jsonNode.fields();
        while (payloadIterator.hasNext()) {
            Map.Entry<String, JsonNode> entry = payloadIterator.next();
            if (entry.getKey().equals("last")) {
                last = entry.getValue().asLong();
            } else if (entry.getValue().isArray()) {
                for (JsonNode node : entry.getValue()) {
                    CandleStick cs = p.getCodec().treeToValue(node, CandleStick.class);
                    candleSticks.add(cs);
                }
            }
        }

        return new OhclPayload(candleSticks, last);
    }

}

I changed the OhclResponse to:

public class OhlcResponse extends Response<OhclPayload> {
}

And insert a OhlcPayload class for the deserializer:

@JsonDeserialize(using = OhlcDeserializer.class)
public class OhclPayload {

    private List<CandleStick> candleSticks;

    private Long last;
    // getters and setters
}

Thats all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM