反序列化JSON - 嵌套列表和所需的键作为值

时间:2017-07-18 15:47:03

标签: java json key-value json-deserialization

我需要使用货币作为值在Java 1.7中对此进行反序列化:

"prices": {
    "USD": [
        [1, "1.99000"],
        [100, "1.89000"],
        [1000, "1.40500"]
    ],
    "EUR": [
        [1, "1.53000"],
        [100, "1.45000"],
        [1000, "1.08350"]
    ]
}

(来源是此REST API https://octopart.com/api/docs/v3/rest-api#notes-partoffer.prices

我的目标是通过属性货币,价格中断和价格为每个价格获取对象。

我无法弄清楚如何执行此操作。我尝试了以下方法:

  1. 反序列化整个"价格"作为进一步处理的字符串(没有工作,得到了无法从START_OBJECT标记中反序列化java.lang.String的实例 - 显然,因为这是JSON_OBJECT)

  2. 反序列化

    LinkedHashMap<String, LinkedHashMap<Integer, String>>>
    
  3. (受此Deserializing Jackson by using a key as value启发,但也没有工作)

    1. 使用这样的包装器:

      public class PartOfferPriceWrapper {
      
      private LinkedHashMap<String, List<Entry<Integer, String>>> prices;
      
      }
      
    2. 然后处理如下:

      List<PartOfferPrice> partOfferPriceList = new ArrayList<PartOfferPrice>();
      
        for (Entry<String, List<Entry<Integer, String>>> currencyEntry : 
          partOfferPrices.entrySet()) {
      
            for (Entry<Integer, String> priceEntry : currencyEntry.getValue()) {
              PartOfferPrice partOfferPrice = new PartOfferPrice();
      
              partOfferPrice.setOffer_id(partOfferId);
              partOfferPrice.setCurrency(currencyEntry.getKey());
              partOfferPrice.setPriceBreak(priceEntry.getKey());              
      
              partOfferPrice.setPrice(Double.parseDouble(priceEntry.getValue()));
      
              partOfferPriceList.add(partOfferPrice);
              }
      

      这看起来很好,但结果是空的(它是更大响应的一部分,阅读Response.readEntity成功)。我找不到任何其他方法来处理这个问题(一些自定义反序列化程序?)。

      修改

      我尝试按照tima的建议使用自定义反序列化器:

          public class PartOfferPricesWrapperDeserializer extends 
          JsonDeserializer<PartOfferPricesWrapper> {
      
          public PartOfferPricesWrapperDeserializer() { super(); }
      
          @Override
          public PartOfferPricesWrapper deserialize(JsonParser jsonParser, 
              DeserializationContext context) throws IOException, 
              JsonProcessingException {
      
              PartOfferPricesWrapper partOfferPricesWrapper = new 
              PartOfferPricesWrapper();
              List<PartOfferPrice> priceList = new ArrayList<PartOfferPrice>();
      
              JsonNode node = jsonParser.readValueAsTree();
      
              Iterator<Entry<String, JsonNode>> nodes = 
              node.get("prices").fields();
      
              while (nodes.hasNext()) {               
                  Map.Entry<String, JsonNode> entry = nodes.next();               
                  for (JsonNode tempNode : entry.getValue()) {
      
                      PartOfferPrice price = new PartOfferPrice();
                      price.setCurrency(entry.getKey());
      
                      for (int i = 0; i < tempNode.size(); i++) {
      
                          if (tempNode.get(i).isInt()) {
                              price.setPriceBreak(tempNode.get(i).intValue());                            
                          }
                          else
                          {
                              price.setPrice(tempNode.get(i).asDouble());                         
                          }                                           
                      }
      
                  priceList.add(price);
                  }
              }
              partOfferPricesWrapper.setPrices(priceList);
              return partOfferPricesWrapper;
          }
      }
      

      将此添加到处理程序方法:

          SimpleModule module = new SimpleModule();
          module.addDeserializer(PartOfferPricesWrapper.class, new 
          PartOfferPricesWrapperDeserializer());
          objectMapper.registerModule(module);          
      
          objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, 
          false);
          partsMatchResponse = objectMapper.readValue(target.getUri().toURL(), 
          PartsMatchResponse.class);
      

      如果回复仅包含&#34;价格&#34; node,但现在我在node.get上得到NullPointerException(&#34;价格&#34;)。fields();它可能试图解析整个响应,但我只需要将自定义解串器用于&#34;价格&#34;部分。它有可能吗?

      非常感谢。

1 个答案:

答案 0 :(得分:1)

是的,自定义反序列化器可以工作。

class CustomDeserializer extends JsonDeserializer<Prices> {

    public CustomDeserializer() { super(); }

    @Override
    public Prices deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
        JsonNode node = jsonParser.readValueAsTree();

        Iterator<Entry<String, JsonNode>> nodes = node.get("prices").fields();

        while (nodes.hasNext()) {
            Map.Entry<String, JsonNode> entry = nodes.next();

            System.out.println(entry.getKey());

            for (JsonNode tempNode : entry.getValue()) {
                for (int i = 0; i < tempNode.size(); i++) {
                    System.out.println(tempNode.get(i).getClass() + "\t" + tempNode.get(i));
                }
            }

            System.out.println();
        }

        return null;
    }
}

输出

USD
class com.fasterxml.jackson.databind.node.IntNode   1
class com.fasterxml.jackson.databind.node.TextNode  "1.99000"
class com.fasterxml.jackson.databind.node.IntNode   100
class com.fasterxml.jackson.databind.node.TextNode  "1.89000"
class com.fasterxml.jackson.databind.node.IntNode   1000
class com.fasterxml.jackson.databind.node.TextNode  "1.40500"

EUR
class com.fasterxml.jackson.databind.node.IntNode   1
class com.fasterxml.jackson.databind.node.TextNode  "1.53000"
class com.fasterxml.jackson.databind.node.IntNode   100
class com.fasterxml.jackson.databind.node.TextNode  "1.45000"
class com.fasterxml.jackson.databind.node.IntNode   1000
class com.fasterxml.jackson.databind.node.TextNode  "1.08350"

您可以在反序列化器中创建所需的对象和结构(Prices是我使用的空类)。

修改

只需对字段进行少量更改即可使用相同的自定义反序列化程序。

前两行如下所示,因为您不需要查找prices节点,因为当它反序列化字段时,它只传递该字段的JSON。其余的线是相同的。

JsonNode node = jsonParser.readValueAsTree();
Iterator<Entry<String, JsonNode>> nodes = node.fields();

然后在你的包装类中:

class PricesWrapper {

    // ...

    @JsonDeserialize(using = CustomDeserializer.class)
    private Prices prices;

    // ...
}