2015-02-20 2 views
2

Как я могу просматривать большие AesonValues? Я знаю, что должна быть строка, в которой я заинтересован в вложенном месте в структуре. Как я могу его найти?Как проверить анализируемое значение Aeson Value?

Пока я знаю, как запросить конструктор и узнать, что это массив. Как я могу копать глубже этого?

> take 20 $ show bt 
"Array (fromList [Obj" 
+0

Возможно, что-то вроде этого '' find_this "' isInfixOf' $ show bt "?? – Sibi

+0

Вы знаете структуру данных, прежде чем разбирать ее? –

+0

@JustinWood Нет. Это большой объект, о котором я ничего не знаю. Некоторые элементы имеют контент, который я ожидаю, и меня интересует только их соседство. Я могу «grep» их, но я надеюсь, что GHCI - лучший инструмент для поиска своего пути, чем текстовый редактор, после довольно печатного контента. – sevo

ответ

3

Пакет 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) 

Мы можем начать с определения линзы Fold, извлекающий немедленного ребенка Values данного JSON Value:

vchildren :: Fold Value Value 
vchildren = folding $ \v -> case v of 
    Object o -> toList o 
    Array a -> toList a 
    _ -> [] 

folding является функцией от lens, что создает Fold из функции, возвращающего список. Список Value s, в нашем случае.

Мы можем объединить vchildren с universeOf функцией от Control.Lens.Plated, чтобы получить функцию, которая извлекает все транзитивные потомки Value, в том числе себя:

allValues :: Value -> [Value] 
allValues = universeOf vchildren 

И эта функция извлекает все тексты, содержащиеся в Value. Он использует _String призму из Data.Aeson.LensPrism немного, как «овеществленная» шаблон, который может быть передан вокруг):

allTexts :: Value -> [Text] 
allTexts = toListOf (folding allValues . _String) 

Control.Lens.Plated также имеет интересные функции, такие как paraOf, которые позволяют строить «paramorphims». Параморфизм - это «контролируемое разрушение» древовидной структуры, исходящей из листьев, и построение результатов вверх. Например, эта функция

vpara :: (Value -> [r] -> r) -> Value -> r 
vpara = paraOf vchildren 

принимает в качестве первого параметра другой функции, которая принимает «текущий узел» вместе с промежуточными результатами для приведенных ниже узлов, и создает промежуточный результат для текущего узла.

vpara начнет потреблять значение JSON из листьев (промежуточный список результатов для этих узлов - просто []) и продолжается вверх.

Один из вариантов использования vpara является получение списка дорожек в формате JSON, которые заканчиваются в тексте, который соответствует некоторому условию, например:

type Path = [Value] 

pathsThatEndInText :: (Text -> Bool) -> Value -> [Path] 
pathsThatEndInText pred = vpara func 
    where 
    func :: Value -> [[Path]] -> [Path] 
    func [email protected](String txt) _ | pred txt = [[v]] 
    func v [email protected](_:_) = Prelude.map (v:) (Prelude.concat l) 
    func _ _ = [] 

Чтобы получить несколько читаемого описание одного из путей, возвращаемых по 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" 

Наконец, вот пример JSON значение для тестирования вышеуказанных функций в GHCI:

exampleJSON :: Value 
exampleJSON = maybe Null id (preview _Value str) 
    where 
    str = "[{ \"k1\" : \"aaa\" },{ \"k2\" : \"ccc\" }, { \"k3\" : \"ddd\" }]" 

И вот gist.