Есть ли простой способ сделать Json.Decode нечувствительным к регистру в elm (0.18)?
decodeDepartmentDate : Json.Decode.Decoder DepartmentDate
decodeDepartmentDate =
Json.Decode.map6 DepartmentDate
(field "nameOfDay" Json.Decode.string)
(field "orderDate" Convert.datePart)
(field "mealTimeID" Json.Decode.string)
(field "mealTime" Json.Decode.string)
(field "departmentID" Json.Decode.string)
(field "department" Json.Decode.string)
Я хочу иметь возможность использовать один и тот же Elm SPA для нескольких серверов и избегать подобных проблем по умолчанию:
BadPayload "Expecting an object with a field named `nameOfDay` at _[11]
but instead got: {\"NameOfDay\":\"Wednesday\",\"OrderDate\":\"2018-09-05T00:00:00\",
\"MealTimeID\":\"546ccee0-e070-403e-a15b-63f4e1366054\",\"MealTime\":\"All Day\",
\"StartTime\":\"2018/06/05 05:04:38\",\"DepartmentID\":\"066a1c9f-97da-487e-b82f-f933b159c042\",
\"Department\":\"Side walk\"}"
Спасибо
Насколько мне известно, готового решения для этого нет. Но вы можете сделать это самостоятельно!
Самый простой способ - это, вероятно, просто сгенерировать различные оболочки и сделать свой собственный декодер field
с помощью oneOf
:
myField name decoder =
Decode.oneOf
[ Decode.field name decoder
, Decode.field (String.toLower) decoder
]
Другой подход заключался бы в декодировании объекта в виде пар ключ / значение без декодирования значений, преобразования ключей и последующего его перекодирования, чтобы иметь возможность использовать на нем существующие декодеры JSON:
lowerCaseKeys =
Decode.keyValuePairs Decode.value
|> Decode.map (List.map (\(key, value) -> (String.toLower key, value)))
|> Decode.map (Encode.object)
Но поскольку значение теперь обернуто в Decoder
, вам придется использовать для этого decodeValue
, и в конечном итоге получится Result
с двойной оболочкой, что не очень хорошо. Возможно, мне не хватает элегантного способа сделать эту работу.
Вместо этого, кажется, лучше не перекодировать его, а просто создать собственный декодер field
для работы с dict. Это также позволит вам игнорировать регистр на указанных вами ключах.
lowerCaseKeys : Decode.Decoder (Dict.Dict String Decode.Value)
lowerCaseKeys =
Decode.keyValuePairs Decode.value
|> Decode.map (List.map (\( key, value ) -> ( String.toLower key, value )))
|> Decode.map Dict.fromList
myField : String -> Decode.Decoder a -> Dict.Dict String Decode.Value -> Decode.Decoder a
myField name decode dict =
case Dict.get (String.toLower name) dict of
Just value ->
case Decode.decodeValue decode value of
Ok v ->
Decode.succeed v
Err e ->
e |> Decode.errorToString |> Decode.fail
Nothing ->
Decode.fail "missing key"
result =
Decode.decodeString (lowerCaseKeys |> Decode.andThen (myField "fOO" Decode.int)) """{ "Foo": 42 }"""
Вы можете определить вариант field
без учета регистра.
fieldInsensitive : String -> Decode.Decoder a -> Decode.Decoder a
fieldInsensitive f d =
let
flow = String.toLower f
in
Decode.keyValuePairs Decode.value |> Decode.andThen
(\ l -> l |> List.filter (\(k, v) -> String.toLower k == flow)
|> List.map (\(k, v) -> v)
|> List.head
|> Maybe.map Decode.succeed
|> Maybe.withDefault (Decode.fail "field not found")
) |> Decode.andThen
(\ v -> case Decode.decodeValue d v of
Ok w -> Decode.succeed w
Err e -> Decode.fail (Decode.errorToString e)
)
Это более или менее тот же код, что и ответ @ glennsl, но заключенный в автономную функцию. Преимущество - более простой интерфейс, недостаток - то, что если вы ищете несколько полей в одном и том же объекте, вы будете повторять работу.
Обратите внимание, что этот код принимает довольно произвольное решение, если есть несколько полей с одним и тем же ключом до регистра! Для более надежного кода, возможно, лучше было бы потерпеть неудачу, если ключ существует более одного раза.
Спасибо, искал что-то подобное. Поиграем и посмотрим, как влияет на производительность несколько поисков.