0 | ||| Interface and utilities for marshalling Idris2 values from JSON
  1 | ||| via an intermediate `Value` representation.
  2 | |||
  3 | ||| For regular algebraic data types, implementations can automatically
  4 | ||| be derived using elaborator reflection (see module `Derive.FromJSON`)
  5 | |||
  6 | ||| Operators and functionality strongly influenced by Haskell's aeson
  7 | ||| library
  8 | module JSON.FromJSON
  9 |
 10 | import Data.Either
 11 | import Data.List.Quantifiers as LQ
 12 | import Data.SortedMap
 13 | import Data.Vect.Quantifiers as VQ
 14 | import Derive.Prelude
 15 | import JSON.ToJSON
 16 | import JSON.Option
 17 | import JSON.Encoder
 18 |
 19 | %language ElabReflection
 20 |
 21 | %default total
 22 |
 23 | --------------------------------------------------------------------------------
 24 | --          Types
 25 | --------------------------------------------------------------------------------
 26 |
 27 | public export
 28 | data JSONPathElement = Key String | Index Bits32
 29 |
 30 | %runElab derive "JSONPathElement" [Show,Eq]
 31 |
 32 | public export
 33 | JSONPath : Type
 34 | JSONPath = List JSONPathElement
 35 |
 36 | public export
 37 | JSONErr : Type
 38 | JSONErr = (JSONPath,String)
 39 |
 40 | public export
 41 | Result : Type -> Type
 42 | Result = Either JSONErr
 43 |
 44 | public export
 45 | Parser : Type -> Type -> Type
 46 | Parser v a = v -> Either JSONErr a
 47 |
 48 | public export
 49 | orElse : Either a b -> Lazy (Either a b) -> Either a b
 50 | orElse r@(Right _) _ = r
 51 | orElse _           v = v
 52 |
 53 | public export
 54 | (<|>) : Parser v a -> Parser v a -> Parser v a
 55 | f <|> g = \vv => f vv `orElse` g vv
 56 |
 57 | public export
 58 | data DecodingErr : Type where
 59 |   JErr      : JSONErr -> DecodingErr
 60 |   JParseErr : ParseError Void -> DecodingErr
 61 |
 62 | %runElab derive "DecodingErr" [Show,Eq]
 63 |
 64 | public export
 65 | DecodingResult : Type -> Type
 66 | DecodingResult = Either DecodingErr
 67 |
 68 | --------------------------------------------------------------------------------
 69 | --          Error Formatting
 70 | --------------------------------------------------------------------------------
 71 |
 72 | ||| Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
 73 | ||| which represents the path relative to some root object.
 74 | export
 75 | formatRelativePath : JSONPath -> String
 76 | formatRelativePath path = format "" path
 77 |
 78 |   where
 79 |     isIdentifierKey : List Char -> Bool
 80 |     isIdentifierKey []      = False
 81 |     isIdentifierKey (x::xs) = isAlpha x && all isAlphaNum xs
 82 |
 83 |     escapeChar : Char -> String
 84 |     escapeChar '\'' = "\\'"
 85 |     escapeChar '\\' = "\\\\"
 86 |     escapeChar c    = singleton c
 87 |
 88 |     escapeKey : List Char -> String
 89 |     escapeKey = fastConcat . map escapeChar
 90 |
 91 |     formatKey : String -> String
 92 |     formatKey key =
 93 |       let chars := fastUnpack key
 94 |        in if isIdentifierKey chars then fastPack $ '.' :: chars
 95 |           else "['" ++ escapeKey chars ++ "']"
 96 |
 97 |     format : String -> JSONPath -> String
 98 |     format pfx []                = pfx
 99 |     format pfx (Index idx :: parts) = format (pfx ++ "[" ++ show idx ++ "]") parts
100 |     format pfx (Key key :: parts)   = format (pfx ++ formatKey key) parts
101 |
102 | ||| Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String',
103 | ||| representing the root object as @$@.
104 | export
105 | formatPath : JSONPath -> String
106 | formatPath path = "$" ++ formatRelativePath path
107 |
108 | ||| Annotate an error message with a
109 | ||| <http://goessner.net/articles/JsonPath/ JSONPath> error location.
110 | export
111 | formatError : JSONPath -> String -> String
112 | formatError path msg = "Error in " ++ formatPath path ++ ": " ++ msg
113 |
114 | export
115 | Interpolation DecodingErr where
116 |   interpolate (JErr (p,s))  = formatError p s
117 |   interpolate (JParseErr x) = interpolate x
118 |
119 | ||| Pretty prints a decoding error. In case of a parsing error,
120 | ||| this might be printed on several lines.
121 | |||
122 | ||| DEPRECATED: Use `interpolate` instead
123 | export %deprecate
124 | prettyErr : (input : String) -> DecodingErr -> String
125 | prettyErr _ = interpolate
126 |
127 | --------------------------------------------------------------------------------
128 | --          Interface
129 | --------------------------------------------------------------------------------
130 |
131 | public export
132 | interface FromJSON a  where
133 |   constructor MkFromJSON
134 |   fromJSON : forall v,obj . Value v obj => Parser v a
135 |
136 | export %inline
137 | decodeVia :
138 |      (0 v : Type)
139 |   -> {auto _ : Value v obj}
140 |   -> {auto _ : FromJSON a}
141 |   -> String
142 |   -> DecodingResult a
143 | decodeVia v s =
144 |   let Right json := parse {v} Virtual s | Left err => Left (JParseErr err)
145 |       Right res  := fromJSON json       | Left p   => Left (JErr p)
146 |    in Right res
147 |
148 | export %inline
149 | decodeEitherVia :
150 |      (0 v : Type)
151 |   -> {auto _ : Value v obj}
152 |   -> {auto _ : FromJSON a}
153 |   -> String
154 |   -> Either String a
155 | decodeEitherVia v s = mapFst interpolate $ decodeVia v s
156 |
157 | export %inline
158 | decodeMaybeVia : (0 v : Type) -> Value v obj => FromJSON a => String -> Maybe a
159 | decodeMaybeVia v = either (const Nothing) Just . decodeVia v
160 |
161 | export %inline
162 | decode : FromJSON a => String -> DecodingResult a
163 | decode = decodeVia JSON
164 |
165 | export %inline
166 | decodeEither : FromJSON a => String -> Either String a
167 | decodeEither = decodeEitherVia JSON
168 |
169 | export %inline
170 | decodeMaybe : FromJSON a => String -> Maybe a
171 | decodeMaybe = decodeMaybeVia JSON
172 |
173 | --------------------------------------------------------------------------------
174 | --          Parsing Utilities
175 | --------------------------------------------------------------------------------
176 |
177 | export %inline
178 | fail : String -> Result a
179 | fail s = Left (Nil,s)
180 |
181 | typeMismatch : Value v obj => String -> Parser v a
182 | typeMismatch expected actual =
183 |   fail $ "expected \{expected}, but encountered \{typeOf actual}"
184 |
185 | unexpected : Value v obj => Parser v a
186 | unexpected actual = fail $ "unexpected \{typeOf actual}"
187 |
188 | export %inline
189 | modifyFailure : (String -> String) -> Result a -> Result a
190 | modifyFailure f = mapFst (map f)
191 |
192 | ||| If the inner 'Parser' failed, prepend the given string to the failure
193 | ||| message.
194 | export %inline
195 | prependFailure : String -> Result a -> Result a
196 | prependFailure = modifyFailure . (++)
197 |
198 | export %inline
199 | prependContext : String -> Result a -> Result a
200 | prependContext name = prependFailure "parsing \{name} failed, "
201 |
202 | export %inline
203 | prependPath : Result a -> JSONPathElement -> Result a
204 | prependPath r elem = mapFst (\(path,s) => (elem :: path,s)) r
205 |
206 | export infixr 9 <?>, .:, .:?, .:!
207 |
208 | ||| Deprecated: Use `prependPath` instead.
209 | export %inline %deprecate
210 | (<?>) : Result a -> JSONPathElement -> Result a
211 | (<?>) = prependPath
212 |
213 | withValue :
214 |      {auto _ : Value v obj}
215 |   -> (type : String)
216 |   -> (v -> Maybe t)
217 |   -> (name : Lazy String)
218 |   -> Parser t a
219 |   -> Parser v a
220 | withValue s get n f val =
221 |   case get val of
222 |     Just v  => f v
223 |     Nothing => prependContext n $ typeMismatch s val
224 |
225 | export %inline
226 | withObject : Value v obj => Lazy String -> Parser obj a -> Parser v a
227 | withObject = withValue "Object" getObject
228 |
229 | export %inline
230 | withBoolean : Value v obj => Lazy String -> Parser Bool a -> Parser v a
231 | withBoolean = withValue "Boolean" getBoolean
232 |
233 | export %inline
234 | withString : Value v obj => Lazy String -> Parser String a -> Parser v a
235 | withString = withValue "String" getString
236 |
237 | export %inline
238 | eqString : Value v obj => Lazy String -> String -> Parser v ()
239 | eqString n s = withString n $ \s' =>
240 |   if s == s' then Right () else fail "expected '\{s}' but got '\{s'}'"
241 |
242 | export %inline
243 | withDouble : Value v obj => Lazy String -> Parser Double a -> Parser v a
244 | withDouble = withValue "Double" getDouble
245 |
246 | export %inline
247 | withInteger : Value v obj => Lazy String -> Parser Integer a -> Parser v a
248 | withInteger = withValue "Integer" getInteger
249 |
250 | export
251 | boundedIntegral :
252 |      {auto _ : Num a}
253 |   -> {auto _ : Value v obj}
254 |   -> Lazy String
255 |   -> (lower : Integer)
256 |   -> (upper : Integer)
257 |   -> Parser v a
258 | boundedIntegral s lo up =
259 |   withInteger s $ \n =>
260 |     if n >= lo && n <= up
261 |       then pure $ fromInteger n
262 |       else fail "integer out of bounds: \{show n}"
263 |
264 | export %inline
265 | withArray : Value v obj => Lazy String -> Parser (List v) a -> Parser v a
266 | withArray = withValue "Array" getArray
267 |
268 | export %inline
269 | withArrayN :
270 |      {auto _ : Value v obj}
271 |   -> (n : Nat)
272 |   -> Lazy String
273 |   -> Parser (Vect n v) a
274 |   -> Parser v a
275 | withArrayN n = withValue "Array of length \{show n}" (getArrayN n)
276 |
277 | ||| See `field`
278 | export
279 | explicitParseField :
280 |      {auto _ : Object obj v}
281 |   -> {auto _ : Value v obj}
282 |   -> Parser v a
283 |   -> obj
284 |   -> Parser String a
285 | explicitParseField p o key =
286 |   case lookup key o of
287 |     Nothing => fail "key \{show key} not found"
288 |     Just v  => p v `prependPath` Key key
289 |
290 | ||| See `fieldMaybe`
291 | export
292 | explicitParseFieldMaybe :
293 |      {auto _ : Object obj v}
294 |   -> {auto _ : Value v obj}
295 |   -> Parser v a
296 |   -> obj
297 |   -> Parser String (Maybe a)
298 | explicitParseFieldMaybe p o key =
299 |   case lookup key o of
300 |     Nothing => Right Nothing
301 |     Just v  =>
302 |       if isNull v then Right Nothing else map Just $ p v `prependPath` Key key
303 |
304 | ||| See `optField`
305 | export
306 | explicitParseFieldMaybe' :
307 |      {auto _ : Object obj v}
308 |   -> {auto _ : Encoder v}
309 |   -> {auto _ : Value v obj}
310 |   -> Parser v a
311 |   -> obj
312 |   -> Parser String a
313 | explicitParseFieldMaybe' p o key =
314 |   case lookup key o of
315 |     Nothing   => p null `prependPath` Key key
316 |     Just v    => p v `prependPath` Key key
317 |
318 | ||| Retrieve the value associated with the given key of an `IObject`.
319 | ||| The result is `empty` if the key is not present or the value cannot
320 | ||| be converted to the desired type.
321 | |||
322 | ||| This accessor is appropriate if the key and value /must/ be present
323 | ||| in an object for it to be valid.  If the key and value are
324 | ||| optional, use `optField` instead.
325 | export %inline
326 | field : Object obj v => Value v obj => FromJSON a => obj -> Parser String a
327 | field = explicitParseField fromJSON
328 |
329 | ||| Deprecated: Use `field` instead
330 | export %deprecate %inline
331 | (.:) : Object obj v => Value v obj => FromJSON a => obj -> Parser String a
332 | (.:) = field
333 |
334 | ||| Retrieve the value associated with the given key of an `IObject`. The
335 | ||| result is `Nothing` if the key is not present or if its value is `Null`,
336 | ||| or `empty` if the value cannot be converted to the desired type.
337 | |||
338 | ||| This accessor is most useful if the key and value can be absent
339 | ||| from an object without affecting its validity.  If the key and
340 | ||| value are mandatory, use `field` instead.
341 | export %inline
342 | fieldMaybe :
343 |      {auto _ : Object obj v}
344 |   -> {auto _ : FromJSON a}
345 |   -> {auto _ : Value v obj}
346 |   -> obj
347 |   -> Parser String (Maybe a)
348 | fieldMaybe = explicitParseFieldMaybe fromJSON
349 |
350 | ||| Retrieve the value associated with the given key of an `Object`
351 | ||| using the given default value in case the key is missing.
352 | export %inline
353 | fieldWithDeflt :
354 |      {auto _ : Object obj v}
355 |   -> {auto _ : FromJSON a}
356 |   -> {auto _ : Value v obj}
357 |   -> obj
358 |   -> Lazy a
359 |   -> Parser String a
360 | fieldWithDeflt o v s = fromMaybe v <$> fieldMaybe o s
361 |
362 | ||| Deprecated: Use `fieldMaybe` instead
363 | export %deprecate %inline
364 | (.:?) :
365 |      {auto _ : Object obj v}
366 |   -> {auto _ : FromJSON a}
367 |   -> {auto _ : Value v obj}
368 |   -> obj
369 |   -> Parser String (Maybe a)
370 | (.:?) = fieldMaybe
371 |
372 | ||| Retrieve the value associated with the given key of an `IObject`
373 | ||| passing on `Null` in case the given key is missing.
374 | |||
375 | ||| This differs from `fieldMaybe` in that it can be used with any converter
376 | ||| accepting `Null` as an input.
377 | export %inline
378 | optField :
379 |      {auto _ : Object obj v}
380 |   -> {auto _ : Encoder v}
381 |   -> {auto _ : FromJSON a}
382 |   -> {auto _ : Value v obj}
383 |   -> obj
384 |   -> Parser String a
385 | optField = explicitParseFieldMaybe' fromJSON
386 |
387 | ||| Deprecated: Use `optField` instead
388 | export %deprecate %inline
389 | (.:!) :
390 |      {auto _ : Object obj v}
391 |   -> {auto _ : Encoder v}
392 |   -> {auto _ : FromJSON a}
393 |   -> {auto _ : Value v obj}
394 |   -> obj
395 |   -> Parser String a
396 | (.:!) = optField
397 |
398 | ||| Function variant of `.:`.
399 | |||
400 | ||| Deprecated: Use `field` instead
401 | export %deprecate %inline
402 | parseField :
403 |      {auto _ : Object obj v}
404 |   -> {auto _ : FromJSON a}
405 |   -> {auto _ : Value v obj}
406 |   -> obj
407 |   -> Parser String a
408 | parseField = field
409 |
410 | ||| Function variant of `.:?`.
411 | |||
412 | ||| Deprecated: Use `fieldMaybe` instead
413 | export %deprecate %inline
414 | parseFieldMaybe :
415 |      {auto _ : Object obj v}
416 |   -> {auto _ : FromJSON a}
417 |   -> {auto _ : Value v obj}
418 |   -> obj
419 |   -> Parser String (Maybe a)
420 | parseFieldMaybe = fieldMaybe
421 |
422 | ||| Function variant of `.:!`.
423 | |||
424 | ||| Deprecated: Use `optField` instead
425 | export %deprecate
426 | parseFieldMaybe' :
427 |      {auto _ : Object obj v}
428 |   -> {auto _ : FromJSON a}
429 |   -> {auto _ : Encoder v}
430 |   -> {auto _ : Value v obj}
431 |   -> obj
432 |   -> Parser String a
433 | parseFieldMaybe' = optField
434 |
435 | --------------------------------------------------------------------------------
436 | --          Implementations
437 | --------------------------------------------------------------------------------
438 |
439 | export
440 | FromJSON Void where
441 |   fromJSON v = fail "Cannot parse Void"
442 |
443 | export
444 | FromJSON () where
445 |   fromJSON = withArray "()" $
446 |     \case Nil    => pure ()
447 |           _ :: _ => fail "parsing () failed, expected empty list"
448 |
449 | export
450 | FromJSON Bool where
451 |   fromJSON = withBoolean "Bool" pure
452 |
453 | export
454 | FromJSON Double where
455 |   fromJSON = withDouble "Double" pure
456 |
457 | export
458 | FromJSON Bits8 where
459 |   fromJSON = boundedIntegral "Bits8" 0 0xff
460 |
461 | export
462 | FromJSON Bits16 where
463 |   fromJSON = boundedIntegral "Bits16" 0 0xffff
464 |
465 | export
466 | FromJSON Bits32 where
467 |   fromJSON = boundedIntegral "Bits32" 0 0xffffffff
468 |
469 | export
470 | FromJSON Bits64 where
471 |   fromJSON = boundedIntegral "Bits64" 0 0xffffffffffffffff
472 |
473 | export
474 | FromJSON Int where
475 |   fromJSON = boundedIntegral "Int" (-0x8000000000000000) 0x7fffffffffffffff
476 |
477 | export
478 | FromJSON Int8 where
479 |   fromJSON = boundedIntegral "Int8" (-0x80) 0x7f
480 |
481 | export
482 | FromJSON Int16 where
483 |   fromJSON = boundedIntegral "Int16" (-0x8000) 0x7fff
484 |
485 | export
486 | FromJSON Int32 where
487 |   fromJSON = boundedIntegral "Int32" (-0x80000000) 0x7fffffff
488 |
489 | export
490 | FromJSON Int64 where
491 |   fromJSON = boundedIntegral "Int64" (-0x8000000000000000) 0x7fffffffffffffff
492 |
493 | export
494 | FromJSON Nat where
495 |   fromJSON = withInteger "Nat" $ \n =>
496 |     if n >= 0 then pure $ fromInteger n
497 |     else fail #"not a natural number: \#{show n}"#
498 |
499 | export
500 | FromJSON Integer where
501 |   fromJSON = withInteger "Integer" pure
502 |
503 | export
504 | FromJSON String where
505 |   fromJSON = withString "String" pure
506 |
507 | export
508 | FromJSON Char where
509 |   fromJSON = withString "Char" $ \str =>
510 |     case strM str of
511 |       (StrCons c "") => pure c
512 |       _ => fail "expected a string of length 1"
513 |
514 | export
515 | FromJSON a => FromJSON (Maybe a) where
516 |   fromJSON v = if isNull v then pure Nothing else Just <$> fromJSON v
517 |
518 | export
519 | FromJSON a => FromJSON (List a) where
520 |   fromJSON = withArray "List" $ traverse fromJSON
521 |
522 | export
523 | FromJSON a => FromJSON (SnocList a) where
524 |   fromJSON = map ([<] <><) . fromJSON
525 |
526 | export
527 | FromJSON a => FromJSON (List1 a) where
528 |   fromJSON = withArray "List1" $
529 |     \case Nil    => fail "expected non-empty list"
530 |           h :: t => traverse fromJSON (h ::: t)
531 |
532 | export
533 | {n : Nat} -> FromJSON a => FromJSON (Vect n a) where
534 |   fromJSON = withArray "Vect \{show n}" $ \vs =>
535 |     case toVect n vs of
536 |       Just vect => traverse fromJSON vect
537 |       Nothing   => fail "expected list of length \{show n}"
538 |
539 | export
540 | FromJSON a => FromJSON b => FromJSON (Either a b) where
541 |   fromJSON = withObject "Either" $ \o =>
542 |     map Left (field o "Left") `orElse`
543 |     map Right (field o "Right")
544 |
545 | export
546 | FromJSON a => FromJSON b => FromJSON (a, b) where
547 |   fromJSON = withArray "Pair" $
548 |     \case [x,y] => [| MkPair (fromJSON x) (fromJSON y) |]
549 |           _     => fail "expected a pair of values"
550 |
551 | readLQ :
552 |      {auto _ : Value v obj}
553 |   -> {auto ps : LQ.All.All (FromJSON . f) ts}
554 |   -> Parser (List v) (LQ.All.All f ts)
555 | readLQ @{_} @{[]} [] = Right []
556 | readLQ @{_} @{_::_} (x :: xs) = [| fromJSON x :: readLQ xs |]
557 | readLQ @{_} @{_::_} [] = fail "list of values too short"
558 | readLQ @{_} @{[]} _    = fail "list of values too long"
559 |
560 | readVQ :
561 |      {auto _ : Value v obj}
562 |   -> {auto ps : VQ.All.All (FromJSON . f) ts}
563 |   -> Parser (List v) (VQ.All.All f ts)
564 | readVQ @{_} @{[]} [] = Right []
565 | readVQ @{_} @{_::_} (x :: xs) = [| fromJSON x :: readVQ xs |]
566 | readVQ @{_} @{_::_} [] = fail "list of values too short"
567 | readVQ @{_} @{[]} _    = fail "list of values too long"
568 |
569 | export
570 | LQ.All.All (FromJSON . f) ts => FromJSON (LQ.All.All f ts) where
571 |   fromJSON = withArray "HList" $ readLQ
572 |
573 | export
574 | VQ.All.All (FromJSON . f) ts => FromJSON (VQ.All.All f ts) where
575 |   fromJSON = withArray "HVect" $ readVQ
576 |
577 | export
578 | FromJSON a => FromJSON (SortedMap String a) where
579 |   fromJSON object =
580 |     case pairs <$> getObject object of
581 |          Just props => maybe (fail "expected an object with string keys and values") Right $
582 |                          foldr (\(key, val), sortedMap => pure $ insert key !(eitherToMaybe $ fromJSON val) !sortedMap)
583 |                                (Just SortedMap.empty)
584 |                                props
585 |          Nothing => fail "expected an object"
586 |
587 | ||| Tries to decode a value encoded as a single field object of the given name.
588 | |||
589 | ||| This corresponds to the `ObjectWithSingleField` option
590 | ||| for encoding sum types.
591 | export
592 | fromSingleField :
593 |      {auto _ : Object obj v}
594 |   -> {auto _ : Value v obj}
595 |   -> (tpe : Lazy String)
596 |   -> Parser (String,v) a
597 |   -> Parser v a
598 | fromSingleField n f = withObject n $ \o => case pairs o of
599 |   [p] => f p
600 |   _   => fail "expected single field object"
601 |
602 | ||| Tries to decode a value encoded as a two-element array with the given
603 | ||| constructor name.
604 | |||
605 | ||| This corresponds to the `TwoElemArray` option
606 | ||| for encoding sum types.
607 | export
608 | fromTwoElemArray :
609 |      {auto _ : Object obj v}
610 |   -> {auto _ : Value v obj}
611 |   -> (tpe : Lazy String)
612 |   -> Parser (String,v) a
613 |   -> Parser v a
614 | fromTwoElemArray n f =
615 |   withArrayN 2 n $ \[x,y] => withString n (\s => f (s,y)) x
616 |
617 | ||| Tries to decode a value encoded as a tagged object with the given
618 | ||| tag and content field, plus tag value.
619 | |||
620 | ||| This corresponds to the `TaggedObject` option
621 | ||| for encoding sum types.
622 | export
623 | fromTaggedObject :
624 |      {auto _ : Object obj v}
625 |   -> {auto _ : Value v obj}
626 |   -> (tpe : Lazy String)
627 |   -> (tagField, contentField : String)
628 |   -> Parser (String,v) a
629 |   -> Parser v a
630 | fromTaggedObject n tf cf f = withObject n $ \o => do
631 |   s <- field o tf
632 |   v <- explicitParseField pure o cf
633 |   f (s,v)
634 |