[英]Elm Json.Decode.Pipeline fails decoding existing field
I have an elm decoder and a test for it.我有一个榆树解码器和一个测试。 The test is passing, but when I use the decoder on the Application, I get a strange error that does not seem consistent with the data passed.
测试通过,但是当我在应用程序上使用解码器时,出现一个奇怪的错误,似乎与通过的数据不一致。 I cannot figure out what is wrong.
我无法弄清楚出了什么问题。 For disclosure, I am new to Elm and this is my first application with it, that is targeted to production.
为了披露,我是 Elm 的新手,这是我的第一个应用程序,它针对生产。
type alias ProviderData =
{ uid : String
, displayName : Maybe String
, photoURL : Maybe String
, email : String
, phoneNumber : Maybe String
, providerId : String
}
type alias TokenManager =
{ apiKey : String
, refreshToken : String
, accessToken : String
, expirationTime : Time.Posix
}
type alias User =
{ uid : String
, displayName : Maybe String
, photoURL : Maybe String
, email : String
, emailVerified : Bool
, phoneNumber : Maybe String
, isAnonymous : Bool
, tenantId : Maybe String
, providerData : List ProviderData
, apiKey : String
, appName : String
, authDomain : String
, stsTokenManager : TokenManager
, redirectEventId : Maybe String
, lastLoginAt : Time.Posix
, createdAt : Time.Posix
}
type alias Model =
{ isUserSignedIn : Bool
, isWaitingForSignInLink : Bool
, user : Maybe User
}
unauthenticatedUser : Model
unauthenticatedUser =
{ isUserSignedIn = False
, isWaitingForSignInLink = False
, user = Nothing
}
decoder : Json.Value -> Model
decoder json =
case Json.decodeValue authDecoder (Debug.log ("Decoding " ++ Json.Encode.encode 4 json) json) of
Ok model ->
model
Err err ->
Debug.log (Debug.toString err) unauthenticatedUser
authDecoder : Json.Decoder Model
authDecoder =
Json.succeed Model
|> required "isUserSignedIn" Json.bool
|> required "isWaitingForSignInLink" Json.bool
|> optional "user" (Json.map Just decodeUser) Nothing
decodeUser : Json.Decoder User
decodeUser =
Json.succeed User
|> required "uid" Json.string
|> optional "displayName" (Json.map Just Json.string) Nothing
|> optional "photoURL" (Json.map Just Json.string) Nothing
|> required "email" Json.string
|> required "emailVerified" Json.bool
|> optional "phoneNumber" (Json.map Just Json.string) Nothing
|> required "isAnonymous" Json.bool
|> optional "tenantId" (Json.map Just Json.string) Nothing
|> required "providerData" (Json.list decodeProviderData)
|> required "apiKey" Json.string
|> required "appName" Json.string
|> required "authDomain" Json.string
|> required "stsTokenManager" decodeTokenManager
|> optional "redirectEventId" (Json.map Just Json.string) Nothing
|> required "lastLoginAt" timestampFromString
|> required "createdAt" timestampFromString
decodeProviderData : Json.Decoder ProviderData
decodeProviderData =
Json.succeed ProviderData
|> required "uid" Json.string
|> optional "displayName" (Json.map Just Json.string) Nothing
|> optional "photoURL" (Json.map Just Json.string) Nothing
|> required "email" Json.string
|> optional "phoneNumber" (Json.map Just Json.string) Nothing
|> required "providerId" Json.string
decodeTokenManager : Json.Decoder TokenManager
decodeTokenManager =
Json.succeed TokenManager
|> required "apiKey" Json.string
|> required "refreshToken" Json.string
|> required "accessToken" Json.string
|> required "expirationTime" timestampFromInt
timestampFromString : Json.Decoder Time.Posix
timestampFromString =
Json.andThen
(\str ->
case String.toInt str of
Just ts ->
Json.succeed (Time.millisToPosix ts)
Nothing ->
Json.fail (str ++ " is not a timetamp")
)
Json.string
timestampFromInt : Json.Decoder Time.Posix
timestampFromInt =
Json.andThen
(\ts ->
Json.succeed (Time.millisToPosix ts)
)
Json.int
unauthenticatedUser : Model
unauthenticatedUser =
{ isUserSignedIn = False
, isWaitingForSignInLink = False
, user = Nothing
}
Failure "Json.Decode.oneOf failed in the following 2 ways:
(1) Problem with the given value:
{
"uid": "",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"emailVerified": true,
"phoneNumber": null,
"isAnonymous": false,
"tenantId": null,
"providerData": [
{
"uid": "joe@mail.com",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"phoneNumber": null,
"providerId": "password"
}
],
"apiKey": "",
"appName": "[DEFAULT]",
"authDomain": "example.firebaseapp.com",
"stsTokenManager": {
"apiKey": "",
"refreshToken": "",
"accessToken": "",
"expirationTime": 1603998440000
},
"redirectEventId": null,
"lastLoginAt": "1603576515267",
"createdAt": "1603573117442",
"multiFactor": {
"enrolledFactors": []
}
}
Expecting an OBJECT with a field named `createdAt`
(2) Problem with the given value:
{
"uid": "",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"emailVerified": true,
"phoneNumber": null,
"isAnonymous": false,
"tenantId": null,
"providerData": [
{
"uid": "joe@mail.com",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"phoneNumber": null,
"providerId": "password"
}
],
"apiKey": "",
"appName": "[DEFAULT]",
"authDomain": "example.firebaseapp.com",
"stsTokenManager": {
"apiKey": "",
"refreshToken": "",
"accessToken": "",
"expirationTime": 1603998440000
},
"redirectEventId": null,
"lastLoginAt": "1603576515267",
"createdAt": "1603573117442",
"multiFactor": {
"enrolledFactors": []
}
}
Expecting null" <internals>: { isUserSignedIn = False, isWaitingForSignInLink = False, user = Nothing }
module Tests exposing (..)
import Expect
import Json.Decode as Json
import Modules.Firebase as Firebase
import Test exposing (..)
import Time
all : Test
all =
describe "Test Firebase"
[ test "Test auth JSON" <|
\_ ->
let
input =
"""
{
"isUserSignedIn": true,
"isWaitingForSignInLink": false,
"user": {
"uid": "xxxxxxxxxxxx",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"emailVerified": true,
"phoneNumber": null,
"isAnonymous": false,
"tenantId": null,
"providerData": [
{
"uid": "joe@mail.com",
"displayName": null,
"photoURL": null,
"email": "joe@mail.com",
"phoneNumber": null,
"providerId": "password"
}
],
"apiKey": "apikey.xxxxxxxxx",
"appName": "[DEFAULT]",
"authDomain": "example.com",
"stsTokenManager": {
"apiKey": "apikey.xxxxxxxxx",
"refreshToken": "refresh.xxxxxxxxxxxx",
"accessToken": "access.xxxxxxxxxxxx",
"expirationTime": 1603825391000
},
"redirectEventId": null,
"lastLoginAt": "1603576515267",
"createdAt": "1603573117442",
"multiFactor": {
"enrolledFactors": []
}
}
}
"""
decodedOutput =
case Json.decodeString Json.value input of
Ok value ->
Ok (Firebase.decoder value)
Err err ->
Err err
in
Expect.equal decodedOutput
(Ok firebaseModel)
]
firebaseModel : Firebase.Model
firebaseModel =
{ isUserSignedIn = True
, isWaitingForSignInLink = False
, user =
Just
{ uid = "xxxxxxxxxxxx"
, displayName = Nothing
, photoURL = Nothing
, email = "joe@mail.com"
, emailVerified = True
, phoneNumber = Nothing
, isAnonymous = False
, tenantId = Nothing
, providerData =
[ { uid = "joe@mail.com"
, displayName = Nothing
, photoURL = Nothing
, email = "joe@mail.com"
, phoneNumber = Nothing
, providerId = "password"
}
]
, apiKey = "apikey.xxxxxxxxx"
, appName = "[DEFAULT]"
, authDomain = "example.com"
, stsTokenManager =
{ apiKey = "apikey.xxxxxxxxx"
, refreshToken = "refresh.xxxxxxxxxxxx"
, accessToken = "access.xxxxxxxxxxxx"
, expirationTime = Time.millisToPosix 1603825391000
}
, redirectEventId = Nothing
, lastLoginAt = Time.millisToPosix 1603576515267
, createdAt = Time.millisToPosix 1603573117442
}
}
Edited at 2020-10-30编辑于 2020-10-30
Trying to debug this issue I started to incrementally build the model, field by field, to understand what was wrong.为了调试这个问题,我开始逐个字段地逐步构建模型,以了解出了什么问题。 The following model and decoder work fine:
以下模型和解码器工作正常:
...
type alias User =
{ uid : String
, displayName : Maybe String
, photoURL : Maybe String
, email : String
, emailVerified : Bool
, phoneNumber : Maybe String
, isAnonymous : Bool
, tenantId : Maybe String
, providerData : List ProviderData
}
...
decodeUser : Json.Decoder User
decodeUser =
Json.succeed User
|> required "uid" Json.string
|> optional "displayName" (Json.nullable Json.string) Nothing
|> optional "photoURL" (Json.nullable Json.string) Nothing
|> required "email" Json.string
|> required "emailVerified" Json.bool
|> optional "phoneNumber" (Json.nullable Json.string) Nothing
|> required "isAnonymous" Json.bool
|> optional "tenantId" (Json.nullable Json.string) Nothing
|> required "providerData" (Json.list decodeProviderData)
...
Note 1- All the other code remains the same.注 1- 所有其他代码保持不变。
Note 2- The input data remains the same.注 2- 输入数据保持不变。
Note 3 - I changed Json.map Just
to Json.nullable
by @5ndG suggestion.注 3 - 我通过 @5ndG 建议将
Json.map Just
更改为Json.nullable
。 I also tried optional "field" (Json.nullable Json.string) Nothing
to required "field" (Json.nullable Json.string)
but the issue persist.我还尝试了
optional "field" (Json.nullable Json.string) Nothing
required "field" (Json.nullable Json.string)
但问题仍然存在。
With this change, the model gets to the expected state, minus the fields I removed.通过此更改,模型将达到预期状态,减去我删除的字段。 By adding the next field,
|> required "apiKey" Json.string
, it starts failing with the same issue.通过添加下一个字段
|> required "apiKey" Json.string
,它开始以相同的问题失败。
I fixed this issue by making the first parameter a string and force my service to pass a JSON string instead of a Json.Decode.Value.我通过将第一个参数设为字符串并强制我的服务传递 JSON 字符串而不是 Json.Decode.Value 来解决此问题。
I change the decorator to:我将装饰器更改为:
decoder : String -> Model
decoder json =
case Json.decodeString Json.value json of
Ok value ->
decodeValue value
Err _ ->
unauthenticatedUser
decodeValue : Json.Value -> Model
decodeValue json =
case Json.decodeValue authDecoder json of
Ok model ->
model
Err _ ->
Debug.log unauthenticatedUser
Not sure why the can't I use the Json.Decode.Value from the service, but this solves the problem.不知道为什么我不能使用服务中的 Json.Decode.Value,但这解决了问题。
Edit: this answer is wrong, so ignore it.编辑:这个答案是错误的,所以忽略它。
I can reproduce your error with this input in the test:我可以在测试中使用此输入重现您的错误:
{
"isUserSignedIn": true,
"isWaitingForSignInLink": false
}
That is, miss out the user
field entirely.也就是说,完全错过了
user
字段。
From reading the docs , I think this is a bug in the optional
function.通过阅读文档,我认为这是
optional
功能中的一个错误。
As described here , you can fix this by replacing the user
decoder with:如上所述在这里,您可以通过更换修复这个
user
用解码器:
|> optional "user" (Json.nullable decodeUser) Nothing
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.