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

标签 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);
}

最佳答案

这是一个为 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}

关于java - 使用 Jackson ObjectMapper 的 JSON 反序列化 Joda Money 导致异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23812273/

相关文章:

java - 使用非 Spring Boot 应用程序进行 Spring Boot 测试

java - 通过http GET从Json对象中提取数据

json - 使用 jsonpath 查询从 Json 对象/数组中过滤名称

javascript - 数据表警告 "unknown parameter 0"

c# - 序列化/反序列化对象 - 字段顺序很重要?

R:将对象序列化到文本文件并再次返回

java - driver.close() 终止测试

java - 在身份验证完成之前获取 j_username

java - 拦截私有(private)字段访问进行延迟加载

C# 有没有办法动态地从序列化中排除成员?