2013-06-24 13 views
11

Ho un gruppo di oggetti JSON nidificati con chiavi arbitrarie.Chiavi JSON arbitrarie con Aeson - Haskell

{ 
    "A": { 
     "B": { 
      "C": "hello" 

     } 
    } 

} 

Dove A, B, C sono sconosciuti prima del tempo. Ognuno di questi tre potrebbe anche avere fratelli.

Mi chiedo se c'è un modo per analizzare questo in un tipo personalizzato con Aeson in un modo elegante. Quello che ho fatto è caricarlo in un Aeson Object.

Come procedere all'implementazione di FromJSON per questo tipo di oggetto JSON ?

Grazie!

Edit:

{ 
    "USA": { 
     "California": { 
      "San Francisco": "Some text" 
     } 
    }, 
    "Canada": { 
     ... 
    } 
} 

Questo dovrebbe compilare in CountryDatabase dove ...

type City   = Map String String 
type Country   = Map String City 
type CountryDatabase = Map String Country 
+1

Non è molto chiaro * come * vorresti analizzare questo JSON. Ha sempre solo 3 chiavi annidate e quindi la stringa? –

+0

Puoi fornire un esempio del tipo personalizzato in cui desideri analizzare? Penso che chiarirei la domanda. –

+0

Domanda aggiornata con un esempio più concreto delle strutture dati. –

risposta

18

è possibile riutilizzare FromJSON istanza di Map String v. Qualcosa come il prossimo:

{-# LANGUAGE OverloadedStrings #-} 

import Data.Functor 
import Data.Monoid 
import Data.Aeson 
import Data.Map (Map) 
import qualified Data.ByteString.Lazy as LBS 
import System.Environment 

newtype City = City (Map String String) 
    deriving Show 

instance FromJSON City where 
    parseJSON val = City <$> parseJSON val 

newtype Country = Country (Map String City) 
    deriving Show 

instance FromJSON Country where 
    parseJSON val = Country <$> parseJSON val 

newtype DB = DB (Map String Country) 
    deriving Show 

instance FromJSON DB where 
    parseJSON val = DB <$> parseJSON val 

main :: IO() 
main = do 
    file <- head <$> getArgs 
    str <- LBS.readFile file 
    print (decode str :: Maybe DB) 

L'output:

[email protected]:/tmp/shum$ cat in.js 
{ 
    "A": { 
     "A1": { 
      "A11": "1111", 
      "A22": "2222" 
     } 
    }, 
    "B": { 
    } 
} 
[email protected]:/tmp/shum$ runhaskell test.hs in.js 
Just (DB (fromList [("A",Country (fromList [("A1",City (fromList [("A11","1111"),("A22","2222")]))])),("B",Country (fromList []))])) 
[email protected]:/tmp/shum$ 

PS: È possibile farlo senza newtype s, li ho usati solo per chiarezza.

+0

Questa risposta è molto utile! Potrebbe essere modificato per ignorare i valori non stringa? (Sostituire '" 1111 "' con '1111', ad esempio, fa fallire l'analisi.) – davidchambers

+0

Come sarebbe un'istanza ToJSON per questo? – AdHominem