web-dev-qa-db-ja.com

JacksonのカスタムJSONで「認識されないトークン」例外を処理する

Jackson jsonパーサー(v2.5.2)を使用して、jsonではないカスタムjsonドキュメントを解析しようとしていますが、それを機能させる方法がわかりません。次のようなjsonドキュメントがあります。

_{
    "test": {
        "one":"oneThing",
        "two": nonStandardThing(),
        "three": true
    }
}
_

ObjectMapperを使用してこれを_Java.util.Map_にマップし、nonStandardThing()をキーtwoのマップに文字列値として追加したいと思います。

ObjectMapper.readValue(json, Map.class)を介してこれを実行すると、例外が発生します。

_com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'nonStandardThing': was expecting 'null', 'true', 'false' or NaN
 at [Source: { "test":{"test1":nonStandardThing(),"test2":"two"}}; line: 1, column: 35]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.Java:1487)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.Java:518)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.Java:2300)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.Java:2277)
_

DeserializationProblemHandlerObjectMapperを登録しようとしましたが、この問題が発生したときに呼び出されません。

これが私が試したことを示すサンプルアプリケーションです。

_import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import Java.io.IOException;
import Java.util.Map;
import Java.util.logging.Level;
import Java.util.logging.Logger;

public class JacksonDeserializerTest {
    private Logger log = Logger.getLogger(JacksonDeserializerTest.class.getName());
    public JacksonDeserializerTest() {
        String validJson = "{ \"test\":{\"test1\":\"one\",\"test2\":\"two\"}}";
        String invalidJson = "{ \"test\":{\"test1\":nonStandardThing(),\"test2\":\"two\"}}";

        ObjectMapper mapper = new ObjectMapper();
        mapper.addHandler(new DeserializationProblemHandler() {
            @Override
            public boolean handleUnknownProperty(DeserializationContext dc, JsonParser jp, JsonDeserializer<?> jd, Object bean, String property) throws IOException, JsonProcessingException {
                System.out.println("Handling unknown property: " + property);
                return false;
            }
        });

        try {
            log.log(Level.INFO, "Valid json looks like: {0}", mapper.readValue( validJson, Map.class).toString());
            log.log(Level.INFO, "Invalid json looks like: {0}", mapper.readValue(invalidJson, Map.class).toString());
        } catch (IOException ex) {
            log.log(Level.SEVERE, "Error parsing json", ex);
        }

    }

    public static void main(String[] args) {
        JacksonDeserializerTest test = new JacksonDeserializerTest();
    }
}
_

出力は次のようになります。

_Apr 24, 2015 1:40:27 PM net.acesinc.data.json.generator.jackson.JacksonDeserializerTest <init>
INFO: Valid json looks like: {test={test1=one, test2=two}}
Apr 24, 2015 1:40:27 PM net.acesinc.data.json.generator.jackson.JacksonDeserializerTest <init>
SEVERE: Error parsing json
com.fasterxml.jackson.core.JsonParseException: Unrecognized token 'nonStandardThing': was expecting 'null', 'true', 'false' or NaN
 at [Source: { "test":{"test1":nonStandardThing(),"test2":"two"}}; line: 1, column: 35]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.Java:1487)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.Java:518)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.Java:2300)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._reportInvalidToken(ReaderBasedJsonParser.Java:2277)
    at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._matchToken(ReaderBasedJsonParser.Java:2129)
_

ハンドラーが呼び出されない理由を誰でも指摘できますか?または、このカスタムjsonドキュメント(jacksonかどうか...)をより適切に解析できる場合は、お知らせください。

12
Andrew Serff

無効な部分はプロパティ(_"two"_)ではなく、値(nonStandardThing())であるため、ハンドラーは呼び出されません。

これを処理する明白な方法は、nonStandardThing()Stringとして渡すことです。つまり、JSONドキュメントを

_{
    "test": {
        "one":"oneThing",
        "two": "nonStandardThing()",
        "three": true
    }
}
_

それが可能性でなければ、やるべきことはあまりありません。カスタムJacksonDeserializerの使用は、値ではなくプロパティに対してのみ有用です。

9
PNS

残念ながら、あなたがリストするコンテンツは有効なJSONではないので、あなたが持っているのは実際にはJSONドキュメントではなく、Javascriptオブジェクトのシリアル化でしょう。 JSONでは、すべての文字列値を二重引用符で囲む必要があります。

ジャクソンはそのようなコンテンツの直接読み取りをサポートしていませんが、SnakeYAMLのようなYAMLパーサーを使用してこれを読み取ることができる場合があります。 Jacksonには https://github.com/FasterXML/jackson-dataformat-yaml/ にYAMLデータ形式モジュールもあります。 YAMLが(ほとんど!)JSONのスーパーセットであることを考えると、おそらくあなたが望むことをすることができます。

2
StaxMan