2014-05-22 53 views
1

使用Dropwizard框架构建API,我使用fasterxml jackson ObjectMapper来解决这个反序列化问题。我正在使用乔达时间和乔达钱。对于Joda时间,定义JodaModule足以解决反序列化问题。但对于Joda Money,JodaModule还不足以解决反序列化问题(如果我错了,请纠正我)。所以,为Joda Time保留JodaModule,我已经创建了Joda Money特定的解串器。JSON反序列化Joda Money使用fasterxml ObjectMapper导致异常

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

回答

4

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

感谢伟大的答案。不幸的是,这款产品模型不太适合我的问题。这是我的错误,没有列出完整答案的所有可能的产品属性。我希望反序列化器将被用于自动反序列化产品的Money属性。产品模型有很多其他属性,JSON将会列出产品的所有属性,并会抛出“无法将START_OBJECT标记的java.lang.String实例反序列化,请注意,谢谢。 –

+0

您能举一个例子JSON具有所有这些属性和异常堆栈跟踪? –

+0

由于我无法添加您作为注释的一部分提出的问题,所以我更新了我的问题,谢谢 –

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);