Copyright | (C) 2016 Ladislav Lhotka |
---|---|
License | BSD3 |
Maintainer | Ladislav Lhotka <lhotka@nic.cz> |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
Zipper interface for JSON Value
.
- data Context
- data Location = Loc Value Context
- anchor :: Value -> Maybe Location
- value :: Location -> Value
- getValue :: Maybe Location -> Value
- child :: Text -> Location -> Maybe Location
- entry :: Int -> Location -> Maybe Location
- sibling :: Text -> Location -> Maybe Location
- previous :: Location -> Maybe Location
- next :: Location -> Maybe Location
- up :: Location -> Maybe Location
- top :: Location -> Maybe Location
- replace :: Value -> Location -> Maybe Location
- addSibling :: Text -> Value -> Location -> Maybe Location
- addBefore :: Value -> Location -> Maybe Location
- addAfter :: Value -> Location -> Maybe Location
Using the zipper interface
This module implements a zipper interface for JSON Value
s pretty
much along the lines of Gérard Huet's original
paper: it defines the Location
type containing
- a JSON
Value
that is the current focus, and - context representing essentially the rest of the JSON document with a hole in the place of the focused value.
Due to the heterogeneity of JSON data, the API is not as uniform as
for "neat" data structures in that some operations are only
intended to work for certain Location
types. Specifically:
child
only works at an object location (i.e. when the focused value is a JSON object),entry
only works at an array location,sibling
andaddSibling
only work at an object member location,previous
,next
,addBefore
andaddAfter
only work at an array entry location.
Typically, the motion and editing operations will be executed as
actions in the Maybe
monad. Such an action may fail if
- it is executed in a wrong location, or
- the value being addressed doesn't exist (e.g. executing
previous
if the focus is on the first entry of an array).
Example
Decode a simple JSON document and put it into the top-level zipper context:
>>>
let tloc = decode "{\"root\": {\"foo\": [1,2], \"bar\": true}}" >>= anchor
Move all the way to entry #1 of the foo
array:
>>>
let foo1 = tloc >>= child "root" >>= child "foo" >>= entry 1
The value at this location is number 2:
>>>
getValue foo1
Number 2.0
Add a new entry – number 3 – before that entry, and go back to the top location:
>>>
let tloc' = foo1 >>= addBefore (Number 3) >>= top
Now we can extract and encode the modified JSON document:
>>>
encode $ getValue tloc'
"{\"root\":{\"foo\":[1,3,2],\"bar\":true}}"
Zipper types
Adding and removing context
getValue :: Maybe Location -> Value Source
Return the value that may not be present. Null
represents a
missing value.
Motion primitives
child :: Text -> Location -> Maybe Location Source
When at object location, descend to the child member with the specified name.
entry :: Int -> Location -> Maybe Location Source
When at array location, descend to the entry with the specified position.
sibling :: Text -> Location -> Maybe Location Source
When at object member location, move to the sibling member with the specified name.
previous :: Location -> Maybe Location Source
When at array entry location, move to the preceding entry.
Updates
addSibling :: Text -> Value -> Location -> Maybe Location Source
When at object member location, add a new sibling with the given name
and value, and move the focus to the new sibling. Nothing
is
returned if a sibling of that name already exists.