2017-07-18 1 views
0

je dois désérialiser cela en Java 1.7 avec Jackson, en utilisant la monnaie en tant que valeur:Deserialize JSON - listes imbriquées et clés nécessaires en tant que valeur

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

(source est cette API REST https://octopart.com/api/docs/v3/rest-api#notes-partoffer.prices)

Mon objectif est d'obtenir des objets pour chaque prix avec des attributs de devise, de rupture de prix et de prix.

Je n'arrive pas à comprendre comment effectuer cela. J'ai essayé les éléments suivants:

  1. deserialize l'ensemble « prix » sous forme de chaîne pour un traitement ultérieur (ne fonctionne pas, obtenu ne peut désérialiser instance de java.lang.String de jeton START_OBJECT - évidemment, puisque cela est JSON_OBJECT)

  2. deserialize comme

    LinkedHashMap<String, LinkedHashMap<Integer, String>>> 
    

(inspiré par ce Deserializing Jackson by using a key as value, mais aussi ne fonctionnait pas)

  1. utiliser un emballage comme celui-ci:

    public class PartOfferPriceWrapper { 
    
    private LinkedHashMap<String, List<Entry<Integer, String>>> prices; 
    
    } 
    

et processus comme:

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

Cela semble bien, mais le résultat est vide (Cela fait partie d'une réponse plus importante, lire avec Response.readEntity a réussi). Je ne peux pas trouver une autre façon de traiter cela (un désérialiseur personnalisé?).

EDIT

J'ai essayé d'utiliser la coutume désérialiseur les conseils suivants 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; 
    } 
} 

ajouter ceci à la méthode de gestionnaire:

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

Ce qui serait probablement travailler si la réponse contient seulement "prix" noeud, mais maintenant je reçois NullPointerException sur node.get ("prix"). fields(); Il essaie probablement d'analyser la réponse entière, mais je dois utiliser le désérialiseur personnalisé uniquement pour la partie "prix". Est-ce que c'est en quelque sorte possible?

Merci beaucoup.

Répondre

0

Oui, un désérialiseur personnalisé fonctionnerait.

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

Sortie

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" 

Vous pouvez créer l'objet et de la structure que vous voulez dans le désérialiseur (Prices est une classe vide je).

EDIT

Vous pouvez utiliser le même désérialiseur personnalisé juste pour le terrain avec un petit changement. Les deux premières lignes se présentent comme ci-dessous, car vous n'avez pas besoin de rechercher le noeud prices car, lorsqu'il désérialise le champ, il ne transmet que le JSON de ce champ. Le reste des lignes sont les mêmes.

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

Ensuite, dans votre classe wrapper:

class PricesWrapper { 

    // ... 

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

    // ... 
} 
+0

Ok merci, je vais essayer. Mais puisque Price n'est qu'une petite partie de la réponse (est liée à PriceOffer, qui est en rapport avec Part, mais tout est un morceau de données), il semble que je devrais désérialiser la réponse entière? – budul

+0

@budul vous êtes les bienvenus. J'ai édité la question pour une désérialisation de champ. – tima

+1

Tima, ça marche! J'apprécie vraiment votre aide depuis que j'ai passé presque deux jours à essayer de la résoudre et aujourd'hui je devrais présenter quelques résultats ... Une seule chose m'ennuie - que je n'ai pas pu résoudre par moi-même :-) (et j'ai même dû m'inscrire à SO ...). Merci beaucoup. – budul