2013-06-24 3 views
11

Ich habe eine Reihe von verschachtelten JSON-Objekte mit beliebigen Schlüsseln.Arbitrary JSON Schlüssel mit Aeson - Haskell

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

     } 
    } 

} 

Wo A, B, C vor der Zeit nicht bekannt sind. Jeder dieser drei könnte auch Geschwister haben.

Ich frage mich, ob es eine Möglichkeit gibt, dies in einen benutzerdefinierten Typ mit Aeson in einige elegante Weise zu analysieren. Was ich getan habe, ist es in ein Aeson Object zu laden.

Wie würden Sie die FromJSON für diese Art von JSON Objekt implementieren?

Danke!

bearbeiten:

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

Dies sollte zu CountryDatabase kompilieren, wo ...

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

Es ist nicht wirklich klar * wie * möchten Sie diesen JSON parsen. Hat es immer nur 3 verschachtelte Schlüssel und dann die Zeichenfolge? –

+0

Können Sie ein Beispiel für den benutzerdefinierten Typ angeben, in den Sie einlesen möchten? Ich denke, das würde die Frage klären. –

+0

Frage aktualisiert mit einem konkreteren Beispiel der Datenstrukturen. –

Antwort

18

Sie können FromJSON Instanz Map String v wiederzuverwenden. So etwas wie die nächste:

{-# 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) 

Der Ausgang:

[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: Sie können es tun, ohne newtype s, ich habe sie nur für Klarheit.

+0

Diese Antwort ist sehr hilfreich! Könnte dies geändert werden, um Nicht-String-Werte zu ignorieren? (Das Ersetzen von "1111" durch "1111" zum Beispiel führt dazu, dass die Analyse fehlschlägt.) – davidchambers

+0

Wie würde eine ToJSON-Instanz dafür aussehen? – AdHominem