Как декодировать помеченные типы объединения в Elm?

#json #elm #decoder

#json #elm #декодер

Вопрос:

Если у меня есть определенный помеченный тип объединения, как Shape здесь, как бы я сконструировал для него JSON-декодер в Elm?

 type alias Rectangle = { width : Int, height : Int }

type alias Circle = { radius: Int }

type Shape 
    = ShapeRectangle Rectangle 
    | ShapeCircle Circle
  

Ответ №1:

Учитывая, что ваш JSON выглядит как

{ "radius" : 10 }

или

{ "width" : 20, "height" : 15}

Тогда это сработает

 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)
  

Пара дополнительных моментов: я часто добавляю поля «тип» и «тег», чтобы помочь устранить неоднозначность, когда у меня есть типы данных с общими именами полей. Тогда JSON выглядит следующим образом

{ "type":"shape", "tag":"circle", "radius":10 }

Также, я думаю, := будет заменен на field в предстоящем выпуске 0.18.

С уважением,

Майкл

Ответ №2:

Ответ Мишеля Тома пролил здесь отличный свет.

Вы можете помечать декодированные значения с помощью Json.Decode.map или andThen вот так:

 `andThen` x -> decode (MyTag x)
  

Здесь используется решение, использующее andThen и Json.Декодировать.Конвейер

 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