使用Jackson ObjectMapper的JSON反序列化Joda Money导致异常

时间:2014-05-22 16:18:56

标签: java json serialization jackson

使用Dropwizard框架构建API,我使用Jackson ObjectMapper遇到了这个反序列化问题。我正在使用Joda Time和Joda Money。对于Joda Time,定义JodaModule足以解决反序列化问题。但是对于Joda Money来说,JodaModule还不足以解决反序列化问题(如果我错了,请纠正我)。因此,为Joda Time保留JodaModule,我创建了Joda Money特定的反序列化器。

public class JodaMoneyDeserializer extends JsonDeserializer<Money> {

    @Override
    public Money deserialize(JsonParser parser, DeserializationContext context) throws IOException, JsonProcessingException {
        String text = parser.getText();
        return Money.parse(text);
    }

}

对于产品型号,我为Money添加了反序列化器。如果我需要定义其他任何内容,请告诉我。

public class Product {
    ...
    private Money price;
    ...

    ...
    @JsonDeserialize(using=JodaMoneyDeserializer.class)
    public void setPrice(Money price) {
        this.price = price;
    }
    ...
}

这将尝试仅解析“{”并将抛出错误。

非常感谢任何提示。

如果您需要其他信息,请与我们联系。

谢谢!

**更新** 这是JSON示例。

{
    "id": 15,
    "productTypeId": 1,
    "code": "XYZK",
    "name": "PRODUCT - XYZK",
    "status": true,
    "visible": true,
    "createdAt": 1400572157000,
    "updatedAt": 1398995061000,
    "description": "description of product",
    "designator": "XYZK",
    "number": "9.032",
    "ingredients": "ingredient 1, ingredient 2, ingredient 3",
    "size": null,
    "weight": 0,
    "googleProductCategory": "Health > Personal Care > Color",
    "metaDescription": null,
    "metaKeyword": null,
    "metaTitle": null,
    "price": {
        "scale": 2,
        "amount": 19.95,
        "positive": true,
        "positiveOrZero": true,
        "negativeOrZero": false,
        "amountMajor": 34,
        "amountMajorLong": 34,
        "amountMajorInt": 34,
        "amountMinor": 3495,
        "amountMinorLong": 3495,
        "amountMinorInt": 3495,
        "minorPart": 95,
        "currencyUnit": {
            "code": "USD",
            "numericCode": 840,
            "decimalPlaces": 2,
            "numeric3Code": "840",
            "countryCodes": [
                "AS",
                "US",
                "EC",
                "MP",
                "TL",
                "VI",
                "VG",
                "GU",
                "SV",
                "MH",
                "PW",
                "PR",
                "FM",
                "TC"
            ],
            "pseudoCurrency": false,
            "symbol": "$",
            "currencyCode": "USD",
            "defaultFractionDigits": 2
        },
        "zero": false,
        "negative": false
    },
    "subPrice": {
        "scale": 2,
        "amount": 0,
        "positive": false,
        "positiveOrZero": true,
        "negativeOrZero": true,
        "amountMajor": 0,
        "amountMajorLong": 0,
        "amountMajorInt": 0,
        "amountMinor": 0,
        "amountMinorLong": 0,
        "amountMinorInt": 0,
        "minorPart": 0,
        "currencyUnit": {
            "code": "USD",
            "numericCode": 840,
            "decimalPlaces": 2,
            "numeric3Code": "840",
            "countryCodes": [
                "AS",
                "US",
                "EC",
                "MP",
                "TL",
                "VI",
                "VG",
                "GU",
                "SV",
                "MH",
                "PW",
                "PR",
                "FM",
                "TC"
            ],
            "pseudoCurrency": false,
            "symbol": "$",
            "currencyCode": "USD",
            "defaultFractionDigits": 2
        },
        "zero": true,
        "negative": false
    },
    "priceGroupId": 1,
    "ignoreFulfillment": false,
    "upc": "2394823409820",
    "productSKU": "XYZK",
    "boxSKUInitial": "12345",
    "boxSKURefill": "12345",
    "urlKey": "capri-blonde",
    "isAddon": false,
    "extendedInfoJson": null
}

我得到的例外。

com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_OBJECT token
...
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:164)
    at com.fasterxml.jackson.databind.DeserializationContext.mappingException(DeserializationContext.java:749)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:55)
    at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:12)
    at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3025)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1637)
    at com.fasterxml.jackson.core.JsonParser.readValueAs(JsonParser.java:1346)
    at com.madisonreed.monocle.dao.mapper.deserializer.MoneyDeserializer.deserialize(MoneyDeserializer.java:26)
    at com.madisonreed.monocle.dao.mapper.deserializer.MoneyDeserializer.deserialize(MoneyDeserializer.java:18)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:538)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:332)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1058)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:268)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3053)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2148)
    at com.madisonreed.monocle.resources.ProductResourceTest.testGetProduct(ProductResourceTest.java:127)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)
    at io.dropwizard.testing.junit.DropwizardAppRule$1.evaluate(DropwizardAppRule.java:55)
    at org.junit.rules.RunRules.evaluate(RunRules.java:18)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:123)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:104)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:164)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:110)
    at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:175)
    at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcessWhenForked(SurefireStarter.java:107)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)

这是我注册解串器的方法。

SimpleModule module = new SimpleModule();
module.addDeserializer(Money.class, new MoneyDeserializer());
mapper = new ObjectMapper();
mapper.registerModule(module);
mapper.registerModule(new JodaModule());

这是我反序列化产品对象的地方。

public void testGetProduct() {
    System.out.println("getProduct");
    ClientResponse response = client.resource(String.format("http://localhost:%d/api/v1/products/15", RULE.getLocalPort())).get(ClientResponse.class);
    Product product = null;
    Boolean validJson = false;
    if (response.getStatus() == 200) {
        String productJSON = response.getEntity(String.class);
        validJson = JacksonJsonUtility.isValidJSON(productJSON);
        System.out.println(productJSON);
        try {
            product = mapper.readValue(productJSON, Product.class);
        } catch (IOException ex) {
            ex.printStackTrace();
            Logger.getLogger(ProductResourceTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    assertTrue(validJson);
}

2 个答案:

答案 0 :(得分:5)

这是一个为Joda Money类型注册序列化器和反序列化器的示例。所有Money对象都转换为JSON字符串。

public class JacksonJodaMoney {

    public static class Product {
        public final Money price;

        @JsonCreator
        public Product(@JsonProperty("price") Money price) {
            this.price = price;
        }

        @Override
        public String toString() {
            return "Product{" +
                    "price=" + price +
                    '}';
        }
    }

    private static class MoneySerializer extends StdSerializer<Money> {
        protected MoneySerializer() {
            super(Money.class);
        }

        @Override
        public void serialize(Money value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
            jgen.writeString(value.toString());
        }
    }

    private static class MoneyDeserializer extends StdDeserializer<Money> {
        protected MoneyDeserializer() {
            super(Money.class);
        }

        @Override
        public Money deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
            return Money.parse(jp.readValueAs(String.class));
        }
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        Product value = new Product(Money.of(CurrencyUnit.EUR, 40.55));
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Money.class, new MoneyDeserializer());
        module.addSerializer(Money.class, new MoneySerializer());
        mapper.registerModule(module);
        String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(value);
        System.out.println(json);
        System.out.println(mapper.readValue(json, Product.class));
    }
}

输出:

{
  "price" : "EUR 40.55"
}
Product{price=EUR 40.55}

答案 1 :(得分:0)

您可以使用此MoneyDeserializer,然后注册此反序列化程序

public class MoneyDeserializer extends JsonDeserializer<Money> {

@Override
public Money deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {

    String currencyCode = "GBP";
    ObjectMapper mapper = (ObjectMapper) jp.getCodec();
    ObjectNode root = mapper.readTree(jp);
    DoubleNode amountNode = (DoubleNode) root.findValue("amount");
    String amount = null;
    if (null != amountNode) {
        amount = amountNode.asText();
    }
    JsonNode currencyUnitNode = root.get("currencyUnit");
    JsonNode currencyCodeNode = currencyUnitNode.get("currencyCode");
    currencyCode = currencyCodeNode.textValue();
    if (StringUtils.isBlank(amount) || StringUtils.isBlank(currencyCode)) {
        throw new IOException("unable to parse json");
    }
    return Money.parse(currencyCode + " " + amount);
}}

然后注册反序列化器:

    ObjectMapper mapper = new ObjectMapper();
    JodaModule module = new JodaModule();
    module.addDeserializer(Money.class, new MoneyDeserializer());
    mapper.registerModule(module);
    YourClass yourclass= mapper.readValue(jsonString, YourClass.class);