2016-03-19 4 views
0

j'ai données JSON qui peuvent soit ressembler à cecivariables de type en instance décélérations (Aeson)

{ 
"items": [Day], 
"pageCount": Int, 
"totalCount": Int 
} 

ou cette

{ 
"items": [Order], 
"pageCount": Int, 
"totalCount": Int 
} 

J'ai essayé de créer un type de données pour la unvaried champs à utiliser avec FromJSON, mais je n'ai pas été en mesure de trouver la bonne façon de le faire, tout en passant par une variété d'erreurs. Ceci est mon code dans son état actuel

--{-# LANGUAGE FlexibleInstances #-} 
--{-# LANGUAGE MultiParamTypeClasses #-} 
--{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE OverloadedStrings #-} 
import   Data.Aeson 

data Typed = Typed {typeID::Int,name::String} deriving (Show,Eq) 
data Day = Day {orderCount::Int,lowPrice::Float,highPrice::Float, avgPrice:: Float,volume::Int,date::String} 
data Order = Order {price::Float,isBuy::Bool,location::Typed} deriving (Show,Eq) 
data Market a = Market {items::a,pageCount::Int,totalCount::Int} deriving (Show,Eq) 
-- Can be either Market [Order] or Market [Day] 
instance FromJSON (Market a) where 
    parseJSON (Object x) = Market <$> x .: "items" <*> x .: "pageCount" <*> x .: "totalCount" 
instance FromJSON Order where 
    parseJSON (Object x) = Order <$> x .: "price" <*> x .: "buy" <*> x .: "location" 
instance FromJSON Typed where 
    parseJSON (Object x) = Typed <$> x .: "id" <*> x .: "name" 
instance FromJSON Day where 
    parseJSON (Object x) = Day <$> x .: "orderCount" <*> x .: "lowPrice" <*> x .: "highPrice" 
    <*> x .: "avgPrice" <*> x .: "volume" <*> x .: "date" 

Et ceci est l'erreur actuelle, je reçois

No instance for (FromJSON a) arising from a use of ‘.:’ 
    Possible fix: 
     add (FromJSON a) to the context of the instance declaration 
    In the second argument of ‘(<$>)’, namely ‘x .: "items"’ 
    In the first argument of ‘(<*>)’, namely ‘Market <$> x .: "items"’ 
    In the first argument of ‘(<*>)’, namely 
     ‘Market <$> x .: "items" <*> x .: "pageCount"’ 
+1

Faites ce qu'il vous dit, 'ajouter FromJSON au contexte a':' exemple FromJSON a => FromJSON (marché a) où ... ' – user2407038

+0

Merci. Je ne savais pas ce que cela signifiait, et je ne pensais pas que ce serait aussi simple. C'est ma première tentative d'utilisation d'instances, et je viens de me débarrasser de plusieurs erreurs "Ne pouvant pas correspondre au type" concernant cette instance, donc j'ai pensé que ce serait plus compliqué que cela. –

Répondre

1

Donc, il y a deux problèmes ici. La première est l'erreur de compilation: vous prétendez que vous avez une instance pour Market a qui devrait signifier que vous savez comment analyser la sérialisation JSON d'un marché pour tout choix de a, mais ce n'est pas vraiment possible parce que vous voulez analyser quelque chose de tapez a afin de donner le prix du contenu du champ price.

Nous limitons notre attention à ne considérer que possible a « s que nous savons comment analyser en ajoutant une contrainte à notre déclaration d'instance:

instance FromJSON a => FromJSON (Market a) where 
    ... 

et maintenant tout va bien. Il y a un autre problème cependant, la façon dont nous avons implémenté FromJSON nous voyons qu'il est criblé de correspondances non exhaustives! Essayez d'exécuter ceci avec -Wall pour voir GHC se plaindre chez nous pour cela. Maintenant, le problème est que chaque parseJSON échouera réellement avec une exception (pas un échec d'analyse, comme un échec de "sauter le programme entier") si alimenté autre chose qu'un Object. Cela signifie que nous obtenons un mauvais comportement comme

λ> decode "1.0" :: Maybe Typed 
*** Exception: /home/jozefg/scratch/Aeson.hs:(24,3)-(25,39): Non-exhaustive patterns in function parseJSON 

Pour résoudre ce problème, nous pouvons simplement ajouter une autre clause qui échoue explicitement dans tous les autres cas. La solution la plus productive pourrait être de ne pas écraser ces instances car elles sont toutes simples et utilisent plutôt le support d'aeson pour les génériques par exemple. Voici l'instance fixe pour Order par exemple où je viens d'ajouter une clause supplémentaire

instance FromJSON Order where 
    parseJSON (Object x) = 
    Order <$> x .: "price" <*> x .: "buy" <*> x .: "location" 
    parseJSON _ = mempty