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.Simple.FromJSON
  9 |
 10 | import Data.List.Quantifiers as LQ
 11 | import Data.Vect.Quantifiers as VQ
 12 | import Data.SortedMap
 13 | import Data.Singleton
 14 | import Derive.Prelude
 15 | import JSON.Parser
 16 | import JSON.Simple.Option
 17 | import JSON.Simple.ToJSON
 18 | import Text.ILex
 19 |
 20 | %language ElabReflection
 21 |
 22 | %default total
 23 |
 24 | --------------------------------------------------------------------------------
 25 | --          Types
 26 | --------------------------------------------------------------------------------
 27 |
 28 | public export
 29 | data JSONPathElement = Key String | Index Bits32
 30 |
 31 | %runElab derive "JSONPathElement" [Show,Eq]
 32 |
 33 | public export
 34 | JSONPath : Type
 35 | JSONPath = List JSONPathElement
 36 |
 37 | public export
 38 | JSONErr : Type
 39 | JSONErr = (JSONPath,String)
 40 |
 41 | public export
 42 | Result : Type -> Type
 43 | Result = Either JSONErr
 44 |
 45 | public export
 46 | Parser : Type -> Type -> Type
 47 | Parser v a = v -> Either JSONErr a
 48 |
 49 | public export
 50 | orElse : Either a b -> Lazy (Either a b) -> Either a b
 51 | orElse r@(Right _) _ = r
 52 | orElse _           v = v
 53 |
 54 | public export
 55 | (<|>) : Parser v a -> Parser v a -> Parser v a
 56 | f <|> g = \vv => f vv `orElse` g vv
 57 |
 58 | public export
 59 | data DecodingErr : Type where
 60 |   JErr      : JSONErr -> DecodingErr
 61 |   JParseErr : ParseError Void -> DecodingErr
 62 |
 63 | %runElab derive "DecodingErr" [Show,Eq]
 64 |
 65 | public export
 66 | DecodingResult : Type -> Type
 67 | DecodingResult = Either DecodingErr
 68 |
 69 | --------------------------------------------------------------------------------
 70 | --          Error Formatting
 71 | --------------------------------------------------------------------------------
 72 |
 73 | ||| Format a <http://goessner.net/articles/JsonPath/ JSONPath> as a 'String'
 74 | ||| which represents the path relative to some root object.
 75 | export
 76 | formatRelativePath : JSONPath -> String
 77 | formatRelativePath path = format "" path
 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 : Parser JSON a
135 |
136 | public export
137 | interface FromJSONKey a  where
138 |   constructor MkFromJSONKey
139 |   fromKey : Parser String a
140 |
141 | export %inline
142 | decode : FromJSON a => String -> DecodingResult a
143 | decode s =
144 |   let Right json := parseJSON 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 | decodeEither : FromJSON a => String -> Either String a
150 | decodeEither s = mapFst interpolate $ decode s
151 |
152 | export %inline
153 | decodeMaybe : FromJSON a => String -> Maybe a
154 | decodeMaybe = either (const Nothing) Just . decode
155 |
156 | --------------------------------------------------------------------------------
157 | --          Parsing Utilities
158 | --------------------------------------------------------------------------------
159 |
160 | export
161 | typeOf : JSON -> String
162 | typeOf JNull        = "Null"
163 | typeOf (JBool _)    = "Boolean"
164 | typeOf (JDouble _)  = "Double"
165 | typeOf (JInteger _)  = "Integer"
166 | typeOf (JString _)  = "String"
167 | typeOf (JArray _)   = "Array"
168 | typeOf (JObject _)  = "Object"
169 |
170 | export %inline
171 | fail : String -> Result a
172 | fail s = Left (Nil,s)
173 |
174 | typeMismatch : String -> Parser JSON a
175 | typeMismatch expected actual =
176 |   fail $ "expected \{expected}, but encountered \{typeOf actual}"
177 |
178 | unexpected : Parser JSON a
179 | unexpected actual = fail $ "unexpected \{typeOf actual}"
180 |
181 | export %inline
182 | modifyFailure : (String -> String) -> Result a -> Result a
183 | modifyFailure f = mapFst (map f)
184 |
185 | ||| If the inner 'Parser' failed, prepend the given string to the failure
186 | ||| message.
187 | export %inline
188 | prependFailure : String -> Result a -> Result a
189 | prependFailure = modifyFailure . (++)
190 |
191 | export %inline
192 | prependContext : String -> Result a -> Result a
193 | prependContext name = prependFailure "parsing \{name} failed, "
194 |
195 | export %inline
196 | prependPath : Result a -> JSONPathElement -> Result a
197 | prependPath r elem = mapFst (\(path,s) => (elem :: path,s)) r
198 |
199 | withValue :
200 |      (type : String)
201 |   -> (JSON -> Maybe t)
202 |   -> (name : Lazy String)
203 |   -> Parser t a
204 |   -> Parser JSON a
205 | withValue s get n f val =
206 |   case get val of
207 |     Just v  => f v
208 |     Nothing => prependContext n $ typeMismatch s val
209 |
210 | export %inline
211 | withKey : Parser String a -> Parser String a
212 | withKey f = prependFailure "parsing key failed, " . f
213 |
214 | export %inline
215 | withObject : Lazy String -> Parser (List (String,JSON)) a -> Parser JSON a
216 | withObject = withValue "Object" $ \case JObject ps => Just ps_ => Nothing
217 |
218 | export %inline
219 | withBoolean : Lazy String -> Parser Bool a -> Parser JSON a
220 | withBoolean = withValue "Boolean" $ \case JBool b => Just b_ => Nothing
221 |
222 | export %inline
223 | withString : Lazy String -> Parser String a -> Parser JSON a
224 | withString = withValue "String" $ \case JString s => Just s_ => Nothing
225 |
226 | export
227 | withNull : String -> t -> Parser JSON t
228 | withNull s x JNull = Right x
229 | withNull s _ v     =
230 |   prependContext s $ fail "expexted Null but encountered \{typeOf v}"
231 |
232 | export %inline
233 | eqString : Lazy String -> String -> Parser JSON ()
234 | eqString n s = withString n $ \s' =>
235 |   if s == s' then Right () else fail "expected '\{s}' but got '\{s'}'"
236 |
237 | export %inline
238 | withDouble : Lazy String -> Parser Double a -> Parser JSON a
239 | withDouble =
240 |   withValue "Double" $ \case
241 |     JDouble d  => Just d
242 |     JInteger n => Just (cast n)
243 |     _          => Nothing
244 |
245 | export
246 | withInteger : Lazy String -> Parser Integer a -> Parser JSON a
247 | withInteger = withValue "Integer" $ \case JInteger d => Just d_ => Nothing
248 |
249 | -- Value parser for integers
250 | pint1 : PVal1 q Void Integer
251 | pint1 = value Nothing [(decimal, bytes decimal)]
252 |
253 | export
254 | withIntegerKey : Parser Integer a -> Parser String a
255 | withIntegerKey f =
256 |   withKey $ \s =>
257 |     case parseString pint1 Virtual s of
258 |       Right v => f v
259 |       Left  _ => fail "not an integer: \{s}"
260 |
261 | export
262 | boundedIntegral :
263 |      {auto _ : Num a}
264 |   -> Lazy String
265 |   -> (lower : Integer)
266 |   -> (upper : Integer)
267 |   -> Parser JSON a
268 | boundedIntegral s lo up =
269 |   withInteger s $ \n =>
270 |     if n >= lo && n <= up
271 |        then Right $ fromInteger n
272 |        else fail "integer out of bounds: \{show n}"
273 |
274 | export
275 | boundedIntegralKey :
276 |      {auto _ : Num a}
277 |   -> (lower : Integer)
278 |   -> (upper : Integer)
279 |   -> Parser String a
280 | boundedIntegralKey lo up =
281 |   withIntegerKey $ \n =>
282 |     if n >= lo && n <= up
283 |        then Right $ fromInteger n
284 |        else fail "integer out of bounds: \{show n}"
285 |
286 | export
287 | withArray : Lazy String -> Parser (List JSON) a -> Parser JSON a
288 | withArray = withValue "Array" $ \case JArray v => Just v_ => Nothing
289 |
290 | export
291 | withArrayN :
292 |      (n : Nat)
293 |   -> Lazy String
294 |   -> Parser (Vect n JSON) a
295 |   -> Parser JSON a
296 | withArrayN n = withValue "Array of length \{show n}" $
297 |   \case JArray v => toVect n v_ => Nothing
298 |
299 | ||| See `field`
300 | export
301 | explicitParseField : Parser JSON a -> List (String,JSON) -> Parser String a
302 | explicitParseField p o key =
303 |   case lookup key o of
304 |     Nothing => fail "key \{show key} not found"
305 |     Just v  => p v `prependPath` Key key
306 |
307 | ||| See `fieldMaybe`
308 | export
309 | explicitParseFieldMaybe :
310 |      Parser JSON a
311 |   -> List (String,JSON)
312 |   -> Parser String (Maybe a)
313 | explicitParseFieldMaybe p o key =
314 |   case lookup key o of
315 |     Nothing    => Right Nothing
316 |     Just JNull => Right Nothing
317 |     Just v     => map Just $ p v `prependPath` Key key
318 |
319 | ||| See `optField`
320 | export
321 | explicitParseFieldMaybe' :
322 |      Parser JSON a
323 |   -> List (String,JSON)
324 |   -> Parser String a
325 | explicitParseFieldMaybe' p o key =
326 |   case lookup key o of
327 |     Nothing => p JNull `prependPath` Key key
328 |     Just v  => p v `prependPath` Key key
329 |
330 | ||| Retrieve the value associated with the given key of an `IObject`.
331 | ||| The result is `empty` if the key is not present or the value cannot
332 | ||| be converted to the desired type.
333 | |||
334 | ||| This accessor is appropriate if the key and value /must/ be present
335 | ||| in an object for it to be valid.  If the key and value are
336 | ||| optional, use `optField` instead.
337 | export %inline
338 | field : FromJSON a => List (String,JSON) -> Parser String a
339 | field = explicitParseField fromJSON
340 |
341 | ||| Retrieve the value associated with the given key of an `IObject`. The
342 | ||| result is `Nothing` if the key is not present or if its value is `Null`,
343 | ||| or `empty` if the value cannot be converted to the desired type.
344 | |||
345 | ||| This accessor is most useful if the key and value can be absent
346 | ||| from an object without affecting its validity.  If the key and
347 | ||| value are mandatory, use `field` instead.
348 | export %inline
349 | fieldMaybe : FromJSON a => List (String,JSON) -> Parser String (Maybe a)
350 | fieldMaybe = explicitParseFieldMaybe fromJSON
351 |
352 | ||| Retrieve the value associated with the given key of an `IObject`
353 | ||| passing on `Null` in case the given key is missing.
354 | |||
355 | ||| This differs from `fieldMaybe` in that it can be used with any converter
356 | ||| accepting `Null` as an input.
357 | export %inline
358 | optField : FromJSON a => List (String,JSON) -> Parser String a
359 | optField = explicitParseFieldMaybe' fromJSON
360 |
361 | ||| Retrieve the value associated with the given key of an `IObject`
362 | ||| using the given default value in case the key is missing.
363 | export %inline
364 | fieldWithDeflt : FromJSON a => List (String,JSON) -> Lazy a -> Parser String a
365 | fieldWithDeflt ps v s = fromMaybe v <$> fieldMaybe ps s
366 |
367 | --------------------------------------------------------------------------------
368 | --          Implementations
369 | --------------------------------------------------------------------------------
370 |
371 | export
372 | FromJSON JSON where fromJSON = Right
373 |
374 | export
375 | FromJSON Void where
376 |   fromJSON v = fail "Cannot parse Void"
377 |
378 | export
379 | FromJSON () where
380 |   fromJSON = withArray "()" $
381 |     \case Nil    => Right ()
382 |           _ :: _ => fail "parsing () failed, expected empty list"
383 |
384 | export
385 | FromJSON Bool where
386 |   fromJSON = withBoolean "Bool" Right
387 |
388 | export
389 | FromJSONKey Bool where
390 |   fromKey =
391 |     withKey $
392 |       \case "True"  => Right True
393 |             "False" => Right False
394 |             s       => fail "not a bool: \{s}"
395 |
396 | export
397 | FromJSON Double where
398 |   fromJSON = withDouble "Double" Right
399 |
400 | -- Value parser for floating point numbers
401 | pdbl1 : PVal1 q Void Double
402 | pdbl1 = value Nothing [(jsonDouble, txt jdouble)]
403 |
404 | export
405 | FromJSONKey Double where
406 |   fromKey =
407 |     withKey $ \s =>
408 |       case parseString pdbl1 Virtual s of
409 |         Right v => Right v
410 |         Left  _ => fail "not a floating point number: \{s}"
411 |
412 | export
413 | FromJSON Bits8 where
414 |   fromJSON = boundedIntegral "Bits8" 0 0xff
415 |
416 | export
417 | FromJSON Bits16 where
418 |   fromJSON = boundedIntegral "Bits16" 0 0xffff
419 |
420 | export
421 | FromJSON Bits32 where
422 |   fromJSON = boundedIntegral "Bits32" 0 0xffffffff
423 |
424 | export
425 | FromJSON Bits64 where
426 |   fromJSON = boundedIntegral "Bits64" 0 0xffffffffffffffff
427 |
428 | export
429 | FromJSON Int where
430 |   fromJSON = boundedIntegral "Int" (-0x8000000000000000) 0x7fffffffffffffff
431 |
432 | export
433 | FromJSON Int8 where
434 |   fromJSON = boundedIntegral "Int8" (-0x80) 0x7f
435 |
436 | export
437 | FromJSON Int16 where
438 |   fromJSON = boundedIntegral "Int16" (-0x8000) 0x7fff
439 |
440 | export
441 | FromJSON Int32 where
442 |   fromJSON = boundedIntegral "Int32" (-0x80000000) 0x7fffffff
443 |
444 | export
445 | FromJSON Int64 where
446 |   fromJSON = boundedIntegral "Int64" (-0x8000000000000000) 0x7fffffffffffffff
447 |
448 | export
449 | FromJSONKey Bits8 where
450 |   fromKey = boundedIntegralKey 0 0xff
451 |
452 | export
453 | FromJSONKey Bits16 where
454 |   fromKey = boundedIntegralKey 0 0xffff
455 |
456 | export
457 | FromJSONKey Bits32 where
458 |   fromKey = boundedIntegralKey 0 0xffffffff
459 |
460 | export
461 | FromJSONKey Bits64 where
462 |   fromKey = boundedIntegralKey 0 0xffffffffffffffff
463 |
464 | export
465 | FromJSONKey Int where
466 |   fromKey = boundedIntegralKey (-0x8000000000000000) 0x7fffffffffffffff
467 |
468 | export
469 | FromJSONKey Int8 where
470 |   fromKey = boundedIntegralKey (-0x80) 0x7f
471 |
472 | export
473 | FromJSONKey Int16 where
474 |   fromKey = boundedIntegralKey (-0x8000) 0x7fff
475 |
476 | export
477 | FromJSONKey Int32 where
478 |   fromKey = boundedIntegralKey (-0x80000000) 0x7fffffff
479 |
480 | export
481 | FromJSONKey Int64 where
482 |   fromKey = boundedIntegralKey (-0x8000000000000000) 0x7fffffffffffffff
483 |
484 | export
485 | FromJSON Nat where
486 |   fromJSON = withInteger "Nat" $ \n =>
487 |     if n >= 0 then Right $ fromInteger n
488 |     else fail "not a natural number: \{show n}"
489 |
490 | export
491 | FromJSONKey Nat where
492 |   fromKey = withIntegerKey $ \n =>
493 |     if n >= 0 then Right $ fromInteger n
494 |     else fail "not a natural number: \{show n}"
495 |
496 | export %inline
497 | FromJSON Integer where
498 |   fromJSON = withInteger "Integer" Right
499 |
500 | export %inline
501 | FromJSONKey Integer where
502 |   fromKey = withIntegerKey Right
503 |
504 | export %inline
505 | FromJSON String where
506 |   fromJSON = withString "String" Right
507 |
508 | export %inline
509 | FromJSONKey String where
510 |   fromKey = withKey Right
511 |
512 | export
513 | FromJSON Char where
514 |   fromJSON = withString "Char" $ \str =>
515 |     case strM str of
516 |       StrCons c "" => Right c
517 |       _            => fail "expected a string of length 1"
518 |
519 | export
520 | FromJSONKey Char where
521 |   fromKey = withKey $ \str =>
522 |     case strM str of
523 |       StrCons c "" => Right c
524 |       _            => fail "expected a string of length 1"
525 |
526 | export
527 | FromJSON a => FromJSON (Maybe a) where
528 |   fromJSON JNull = Right Nothing
529 |   fromJSON v     = Just <$> fromJSON v
530 |
531 | export
532 | FromJSON a => FromJSON (List a) where
533 |   fromJSON = withArray "List" $ traverse fromJSON
534 |
535 | export
536 | FromJSON a => FromJSON (SnocList a) where
537 |   fromJSON = map ([<] <><) . fromJSON
538 |
539 | export
540 | FromJSON a => FromJSON (List1 a) where
541 |   fromJSON = withArray "List1" $ \case
542 |     Nil    => fail "expected non-empty list"
543 |     h :: t => traverse fromJSON (h ::: t)
544 |
545 | export
546 | {v : a} -> FromJSON a => ToJSON a => Eq a => FromJSON (Singleton v) where
547 |   fromJSON x =
548 |     fromJSON x >>= \val => case v == val of
549 |       True  => Right (Val v)
550 |       False => fail "Invalid value. Expected \{encode v}"
551 |
552 | sortedMap :
553 |      {auto ord : Ord k}
554 |   -> {auto jk  : FromJSONKey k}
555 |   -> {auto jv  : FromJSON v}
556 |   -> SortedMap k v
557 |   -> Parser (List (String,JSON)) (SortedMap k v)
558 | sortedMap m []            = Right m
559 | sortedMap m ((x,y) :: ps) =
560 |   let Right k' := fromKey x  | Left err => Left err
561 |       Right v' := fromJSON y | Left err => Left err
562 |    in sortedMap (insert k' v' m) ps
563 |
564 | export %inline
565 | Ord k => FromJSONKey k => FromJSON v => FromJSON (SortedMap k v) where
566 |   fromJSON = withObject "SortedMap" (sortedMap empty)
567 |
568 | export
569 | {n : Nat} -> FromJSON a => FromJSON (Vect n a) where
570 |   fromJSON = withArray "Vect \{show n}" $ \vs => case toVect n vs of
571 |     Just vect => traverse fromJSON vect
572 |     Nothing   => fail "expected list of length \{show n}"
573 |
574 | export
575 | FromJSON a => FromJSON b => FromJSON (Either a b) where
576 |   fromJSON = withObject "Either" $ \o =>
577 |     map Left (field o "Left") `orElse`
578 |     map Right (field o "Right")
579 |
580 | export
581 | FromJSON a => FromJSON b => FromJSON (a, b) where
582 |   fromJSON = withArray "Pair" $
583 |     \case [x,y] => [| MkPair (fromJSON x) (fromJSON y) |]
584 |           _     => fail "expected a pair of values"
585 |
586 | readLQ : (ps : LQ.All.All (FromJSON . f) ts) => Parser (List JSON) (All f ts)
587 | readLQ @{[]} [] = Right []
588 | readLQ @{_::_} (x :: xs) = [| fromJSON x :: readLQ xs |]
589 | readLQ @{_::_} [] = fail "list of values too short"
590 | readLQ @{[]} _    = fail "list of values too long"
591 |
592 | readVQ : (ps : VQ.All.All (FromJSON . f) ts) => Parser (List JSON) (All f ts)
593 | readVQ @{[]} [] = Right []
594 | readVQ @{_::_} (x :: xs) = [| fromJSON x :: readVQ xs |]
595 | readVQ @{_::_} [] = fail "list of values too short"
596 | readVQ @{[]} _    = fail "list of values too long"
597 |
598 | export
599 | LQ.All.All (FromJSON . f) ts => FromJSON (All f ts) where
600 |   fromJSON = withArray "HList" $ readLQ
601 |
602 | export
603 | VQ.All.All (FromJSON . f) ts => FromJSON (VQ.All.All f ts) where
604 |   fromJSON = withArray "HVect" $ readVQ
605 |
606 | ||| Tries to decode a value encoded as a single field object of the given name.
607 | |||
608 | ||| This corresponds to the `ObjectWithSingleField` option
609 | ||| for encoding sum types.
610 | export
611 | fromSingleField :
612 |      (tpe : Lazy String)
613 |   -> Parser (String,JSON) a
614 |   -> Parser JSON a
615 | fromSingleField n f = withObject n $
616 |   \case [p] => f p
617 |         _   => fail "expected single field object"
618 |
619 | ||| Tries to decode a value encoded as a two-element array with the given
620 | ||| constructor name.
621 | |||
622 | ||| This corresponds to the `TwoElemArray` option
623 | ||| for encoding sum types.
624 | export
625 | fromTwoElemArray :
626 |      (tpe : Lazy String)
627 |   -> Parser (String,JSON) a
628 |   -> Parser JSON a
629 | fromTwoElemArray n f =
630 |   withArrayN 2 n $ \[x,y] => withString n (\s => f (s,y)) x
631 |
632 | ||| Tries to decode a value encoded as a tagged object with the given
633 | ||| tag and content field, plus tag value.
634 | |||
635 | ||| This corresponds to the `TaggedObject` option
636 | ||| for encoding sum types.
637 | export
638 | fromTaggedObject :
639 |      (tpe : Lazy String)
640 |   -> (tagField, contentField : String)
641 |   -> Parser (String,JSON) a
642 |   -> Parser JSON a
643 | fromTaggedObject n tf cf f = withObject n $ \o => do
644 |   s <- field o tf
645 |   v <- explicitParseField Right o cf
646 |   f (s,v)
647 |