如何解码Elm中标记的联合类型?

时间:2022-04-04 19:42:16

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