Extract the Text from a JSON value String Text wit

2019-08-05 20:44发布

问题:

Here is the definition for a Json Value :

-- | A JSON value represented as a Haskell value.
data Value = Object !Object
           | Array !Array
           | String !Text
           | Number !Scientific
           | Bool !Bool
           | Null
             deriving (Eq, Show)

let value = String "myValue"
looking for => fromString value == "myValue" ??
fromString :: Value -> Text

I'm looking a function like where I could get the Text from String without to do some pattern matching, obviously this function will be unsafe... a fromString like fromJust in Data.Maybe for example... Something in Data.Lens.Aeson ?

回答1:

As Thomas M. DuBuisson implies in the above comment, this sounds like an XY Problem. Still, I'll do my best to nonetheless address the specifics.

Technically, you can trivially write a function with the type Value -> Text, although it requires pattern matching. I realise that the OP requests a function without pattern matching, but please read on:

-- Warning: UNSAFE!
fromString :: Value -> Text
fromString (String s) = s

Such a function compiles, but is unsafe!

*Q53961314 Q53961314> fromString $ String "myValue"
"myValue"
*Q53961314 Q53961314> fromString $ Number 42
"*** Exception: Non-exhaustive patterns in function fromString

While it works for String values, it crashes for any other type of value. While it's technically possible to write and compile unsafe functions like the above, AFAICT it's not considered idiomatic Haskell.

A better alternative is a safe function that returns Maybe Text. This is still easy to write using pattern matching:

fromStringSafe :: Value -> Maybe Text
fromStringSafe (String s) = Just s
fromStringSafe _ = Nothing

This function is total:

*Q53961314 Q53961314> fromStringSafe $ String "myValue"
Just "myValue"
*Q53961314 Q53961314> fromStringSafe $ Number 42
Nothing

If you don't wish to write such a function yourself, but prefer to use lens-aeson, you can use the _String prism:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> String "myValue" ^? _String
Just "myValue"

As you can tell, this is also safe, in that ^? _String returns Maybe Text:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Bool True ^? _String
Nothing
Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Number 42 ^? _String
Nothing

If you really, really want an unsafe function, you can use ^?! _String:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> String "myValue" ^?! _String
"myValue"

It is, not surprisingly, unsafe:

Prelude Data.Aeson Data.Aeson.Lens Control.Lens> Number 42 ^?! _String
"*** Exception: (^?!): empty Fold
CallStack (from HasCallStack):
  error, called at src\\Control\\Lens\\Fold.hs:1285:28 in lens-4.17-7m3etWEBj0P3172qv7LEG9:Control.Lens.Fold
  ^?!, called at <interactive>:9:1 in interactive:Ghci3

I wonder why you're asking about this sort of functionality, though. Is there a specific problem you're trying to solve with Aeson that we can help with?