If I have a certain tagged union type, like Shape
here, how would I construct a JSON decoder for it in Elm?
如果我有一个标记的联合类型,比如Shape这里,我将如何在Elm中为它构造一个JSON解码器?
type alias Rectangle = { width : Int, height : Int }
type alias Circle = { radius: Int }
type Shape
= ShapeRectangle Rectangle
| ShapeCircle Circle
2 个解决方案
#1
10
Given your JSON looks like
鉴于你的JSON看起来像
{ "radius" : 10 }
{“radius”:10}
or
{ "width" : 20, "height" : 15}
{“宽度”:20,“身高”:15}
Then this will do the trick
然后这将成功
import Json.Decode as Json exposing ((:=))
decodeShape : Json.Decoder Shape
decodeShape =
Json.oneOf
[ decodeShapeRectangle
, decodeShapeCircle
]
decodeShapeRectangle : Json.Decoder Shape
decodeShapeRectangle =
Json.map ShapeRectangle <|
Json.object2 Rectangle
("width" := Json.int)
("height" := Json.int)
decodeShapeCircle : Json.Decoder Shape
decodeShapeCircle =
Json.object1 (ShapeCircle << Circle)
("radius" := Json.int)
A couple of additional things: I often add a 'type' and 'tag' field to help disambiguate when I have data types with common field names. The JSON then looks like
还有一些额外的东西:我经常添加一个“类型”和“标记”字段,以帮助消除歧义,当我拥有具有公共字段名称的数据类型时。然后JSON看起来像
{ "type":"shape", "tag":"circle", "radius":10 }
{“type”:“shape”,“tag”:“circle”,“radius”:10}
Also, I think :=
will be replaced by field
in the upcoming 0.18 release.
另外,我认为:=将在即将发布的0.18版本中被字段取代。
Regards,
Michael
#2
1
Michel Thoma's answer shone a great light here.
Michel Thoma的回答在这里引起了广泛关注。
You can tag decoded values using Json.Decode.map
or andThen
like this:
您可以使用Json.Decode.map标记已解码的值,或者像这样:
`andThen` \x -> decode (MyTag x)
Using here is a solution using andThen
and Json.Decode.Pipeline
这里使用的是使用andThen和Json.Decode.Pipeline的解决方案
import Json.Decode exposing ( Decoder, decodeString, int, andThen, oneOf )
import Json.Decode.Pipeline exposing ( decode, required )
import Html
main =
let
decoded = decodeString decodeShape "{ \"radius\": 2 }"
in
case decoded of
Ok shape ->
Html.text <| toString shape
Err error ->
Html.text error
type alias Rectangle = { width : Int, height : Int }
type alias Circle = { radius: Int }
type Shape
= ShapeRectangle Rectangle
| ShapeCircle Circle
decodeShape : Decoder Shape
decodeShape =
oneOf
[ decodeRectangle `andThen` \x -> decode (ShapeRectangle x)
, decodeCircle `andThen` \x -> decode (ShapeCircle x)
]
decodeRectangle : Decoder Rectangle
decodeRectangle =
decode Rectangle
|> required "width" int
|> required "height" int
decodeCircle : Decoder Circle
decodeCircle =
decode Circle
|> required "radius" int
#1
10
Given your JSON looks like
鉴于你的JSON看起来像
{ "radius" : 10 }
{“radius”:10}
or
{ "width" : 20, "height" : 15}
{“宽度”:20,“身高”:15}
Then this will do the trick
然后这将成功
import Json.Decode as Json exposing ((:=))
decodeShape : Json.Decoder Shape
decodeShape =
Json.oneOf
[ decodeShapeRectangle
, decodeShapeCircle
]
decodeShapeRectangle : Json.Decoder Shape
decodeShapeRectangle =
Json.map ShapeRectangle <|
Json.object2 Rectangle
("width" := Json.int)
("height" := Json.int)
decodeShapeCircle : Json.Decoder Shape
decodeShapeCircle =
Json.object1 (ShapeCircle << Circle)
("radius" := Json.int)
A couple of additional things: I often add a 'type' and 'tag' field to help disambiguate when I have data types with common field names. The JSON then looks like
还有一些额外的东西:我经常添加一个“类型”和“标记”字段,以帮助消除歧义,当我拥有具有公共字段名称的数据类型时。然后JSON看起来像
{ "type":"shape", "tag":"circle", "radius":10 }
{“type”:“shape”,“tag”:“circle”,“radius”:10}
Also, I think :=
will be replaced by field
in the upcoming 0.18 release.
另外,我认为:=将在即将发布的0.18版本中被字段取代。
Regards,
Michael
#2
1
Michel Thoma's answer shone a great light here.
Michel Thoma的回答在这里引起了广泛关注。
You can tag decoded values using Json.Decode.map
or andThen
like this:
您可以使用Json.Decode.map标记已解码的值,或者像这样:
`andThen` \x -> decode (MyTag x)
Using here is a solution using andThen
and Json.Decode.Pipeline
这里使用的是使用andThen和Json.Decode.Pipeline的解决方案
import Json.Decode exposing ( Decoder, decodeString, int, andThen, oneOf )
import Json.Decode.Pipeline exposing ( decode, required )
import Html
main =
let
decoded = decodeString decodeShape "{ \"radius\": 2 }"
in
case decoded of
Ok shape ->
Html.text <| toString shape
Err error ->
Html.text error
type alias Rectangle = { width : Int, height : Int }
type alias Circle = { radius: Int }
type Shape
= ShapeRectangle Rectangle
| ShapeCircle Circle
decodeShape : Decoder Shape
decodeShape =
oneOf
[ decodeRectangle `andThen` \x -> decode (ShapeRectangle x)
, decodeCircle `andThen` \x -> decode (ShapeCircle x)
]
decodeRectangle : Decoder Rectangle
decodeRectangle =
decode Rectangle
|> required "width" int
|> required "height" int
decodeCircle : Decoder Circle
decodeCircle =
decode Circle
|> required "radius" int