将JSON解码为Elm的Maybe类型

16

给定以下的JSON:

[
  {
    "id": 0,
    "name": "Item 1",
    "desc": "The first item"
  },
  {
    "id": 1,
    "name": "Item 2"
  }
]

如何将其解码为以下模型:

type alias Model =
    { id : Int
    , name : String
    , desc : Maybe String
    }
3个回答

25
Brian Hicks在JSON解码器方面有一系列文章,你可能想要特别关注向JSON解码器添加新字段(处理当你可能会或可能不会从JSON对象接收到一个字段的情况)。请注意,本文保留了HTML标签。

首先,您可能想使用elm-decode-pipeline包。然后,您可以使用optional函数声明您的desc字段可能不存在。正如Brian在文章中指出的那样,您可以使用核心Json.Decode中的maybe解码器,但它将对任何失败产生Nothing,而不仅仅是null。如果您不想使用管道模块,还有一个nullable解码器可供考虑。

你的解码器可能看起来像这样:

modelDecoder : Decoder Model
modelDecoder =
    decode Model
        |> required "id" int
        |> required "name" string
        |> optional "desc" (Json.map Just string) Nothing

这里是Ellie上的实时示例。


在发布了我的问题之后,进一步的谷歌搜索让我找到了Brian Hicks关于JSON解码器的帖子。当我回来时看到了你的答案,我感到很高兴。很高兴找到一个对他人有帮助的好资源。 - Matthew Rankin
在你的最后一行,我认为你想要(Json.map Just string)而不是Json.map Just int) - Matthew Rankin
谢谢,我已经用 string 和一个 Ellie 示例更新了它。希望能有所帮助! - bdukes
是的!我根据Brian Hicks的信息得出了相同的答案。我将您的答案标记为已接受,因为您先发布了。 - Matthew Rankin
1
从 Elm 0.19 开始,该软件包已更名为 elm-json-decode-pipeline - Yoni Gibbs

18

所以,如果你正在寻找一种不需要依赖于Json.Decode.Pipeline的解决方案。

import Json.Decode as Decode exposing (Decoder)


modelDecoder : Decoder Model
modelDecoder =
    Decode.map3 Model
        (Decode.field "id" Decode.int)
        (Decode.field "name" Decode.string)
        (Decode.maybe (Decode.field "desc" Decode.string))

如果你想使用Model构造函数作为可应用函子来完成此操作(因为你需要超过8个项目)。

import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Extra as Decode


modelDecoder : Decoder Model
modelDecoder =
    Decode.succeed Model
        |> Decode.andMap (Decode.field "id" Decode.int)
        |> Decode.andMap (Decode.field "name" Decode.string)
        |> Decode.andMap (Decode.maybe (Decode.field "desc" Decode.string))

这两个函数都可以与ListDecode.list modelDecoder一起使用。我希望这些应用函数能够成为标准库的一部分,但你需要到所有的* -extra库中去获取这些功能。了解应用函子的工作原理将有助于您更好地理解后续内容,因此建议您阅读相关资料。Decode Pipeline解决方案抽象了这个简单的概念,但当您遇到需要使用Result.andMap或任何其他andMap时,因为您的模块或DSL没有mapN,您将知道如何解决问题。

由于解码器的应用性质,所有字段都应该能够异步并行处理,并且可以获得小的性能提升,而不是像andThen一样同步处理,这适用于您在每个使用andMap而不是andThen的地方。话虽如此,在调试时切换到andThen可以为您提供一个可用的错误位置,当您知道一切正常时,可以将其更改为andMap

在幕后,JSON.Decode.Pipeline使用Json.Decode.map2(即andMap),因此没有性能差异,但使用的DSL略微更加“友好”。


1
这可能是正确的响应。没有必要引入完全独立的依赖项,仅因为您在API中具有此行为。它已经默认支持了。 - jnmandal
andThen 路由的一个优点是您有机会绑定特定的错误消息,但这在调试时通常很有用。 andMap 在技术上应该是可并行化的,因为它是适用的。 - toastal

2
Brian Hicks的文章"Adding New Fields to Your JSON Decoder"帮助我开发了以下内容。要查看一个可工作的示例,请参见Ellie
import Html exposing (..)
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Pipeline as JP
import String

type alias Item =
    { id : Int
    , name : String
    , desc : Maybe String
    }


main =
    Decode.decodeString (Decode.list itemDecoder) payload
        |> toString
        |> String.append "JSON "
        |> text


itemDecoder : Decoder Item
itemDecoder =
    JP.decode Item
        |> JP.required "id" Decode.int
        |> JP.required "name" Decode.string
        |> JP.optional "desc" (Decode.map Just Decode.string) Nothing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接