大きくブラウジングするにはどうすればよいAeson
Values
ですか? 興味のある文字列が構造体のどこかにネストされている必要があることはわかっています。どうすれば見つけられますか?
これまでのところ、コンストラクターを照会する方法しか知らず、それが配列であることがわかりました。どうすればそれ以上深く掘り下げることができますか?
> take 20 $ show bt
"Array (fromList [Obj"
このlens
パッケージには、 JSON のようなツリー状の構造を検査するための便利な機能がありますValue
。lens-aeson
追加の JSON 固有の機能を備えたパッケージもあります。
import Data.Text
import Data.Aeson
import Data.Aeson.Lens (_Value,_String) -- this is from lens-aeson
import Data.Foldable (toList)
import Control.Lens (Fold,folding,universeOf,toListOf,paraOf,preview)
指定された JSONFold
の直接の子を抽出するレンズを定義することから始めることができます。Values
Value
vchildren :: Fold Value Value
vchildren = folding $ \v -> case v of
Object o -> toList o
Array a -> toList a
_ -> []
folding
リストを返す関数を作成する関数ですlens
。Fold
この場合、のリストValue
。
関数 fromと組み合わせvchildren
て、それ自体を含むa のすべての推移的な子孫を抽出する関数を取得できます。universeOf
Control.Lens.Plated
Value
allValues :: Value -> [Value]
allValues = universeOf vchildren
そして、この関数は、に含まれるすべてのテキストを抽出しますValue
。_String
プリズムを使用しますData.Aeson.Lens
(aPrism
は、渡すことができる「具体化された」パターンに少し似ています):
allTexts :: Value -> [Text]
allTexts = toListOf (folding allValues . _String)
Control.Lens.Plated
paraOf
には、「パラモルフィム」を構築できる のような興味深い機能もあります。パラモーフィズムは、葉から始まり、結果を上方向に構築する木のような構造の「制御された破壊」です。たとえば、この関数
vpara :: (Value -> [r] -> r) -> Value -> r
vpara = paraOf vchildren
最初のパラメータとして、「現在のノード」と下のノードの中間結果を受け取る別の関数を取り、現在のノードの中間結果を構築します。
vpara
リーフから JSON 値の消費を開始し (これらのノードの中間結果リストは単純です[]
)、上に進みます。
の使用例の 1 つvpara
は、次のように、ある条件に一致するテキストで終わる JSON 内のパスのリストを取得することです。
type Path = [Value]
pathsThatEndInText :: (Text -> Bool) -> Value -> [Path]
pathsThatEndInText pred = vpara func
where
func :: Value -> [[Path]] -> [Path]
func v@(String txt) _ | pred txt = [[v]]
func v l@(_:_) = Prelude.map (v:) (Prelude.concat l)
func _ _ = []
によって返されたパスの 1 つのある程度読みやすい説明を取得するには、次のようにしpathsThatEndInText
ます。
import qualified Data.HashMap.Strict as HM
import qualified Data.Vector as V
describePath :: Path -> [String]
describePath (v:vs) = Prelude.zipWith step (v:vs) vs
where
step (Object o) next = (unpack . Prelude.head . HM.keys . HM.filter (==next)) o
step (Array a) next = (show . maybe (error "not found") id) (V.elemIndex next a)
step _ _ = error "should not happen"
最後に、ghci で上記の関数をテストするための JSON 値の例を次に示します。
exampleJSON :: Value
exampleJSON = maybe Null id (preview _Value str)
where
str = "[{ \"k1\" : \"aaa\" },{ \"k2\" : \"ccc\" }, { \"k3\" : \"ddd\" }]"
要点は次のとおりです。