How to parse a JSON object?

I have a String containing a JSON object and want to get the content of a property of this JSON object.

The input String could look like this:

"{\"fieldName\":4652007308841189719}"

I tried to use the elm/json/1.1.3 package to parse the JSON, but only got wrong results using this implementation:

module Main exposing (main)

import Browser
import Html
import Json.Decode
import Json.Encode


getJsonSubstringFromField fieldName =
    Json.Decode.decodeString (Json.Decode.field fieldName Json.Decode.value)
        >> Result.map (Json.Encode.encode 0)


main : Html.Html a
main =
    "{\"fieldName\":4652007308841189719}"
        |> getJsonSubstringFromField "fieldName"
        |> Result.withDefault "Failed to decode JSON"
        |> Html.text

(https://ellie-app.com/7Kk2TrLn3qfa1)

Instead of the expected value, this displayed 4652007308841189000.

How can I get the JSON value of the property of an object? The original substring would work for me, but preserving formatting is not necessary.


Edit:
The solution was to use a JSON parser like https://github.com/allenap/elm-json-decode-broken/blob/4b1861f4282b00146ae57a48a5ba0dae442baefb/src/Json/Decode/Broken.elm
The readme in the repository there is more focused on broken JSON, but as @dmy showed, we can configure it to help us parse valid JSON:
(https://ellie-app.com/7KBMdXHBKVba1)

module Main exposing (main)

import Browser
import Html
import Json.Decode
import Json.Decode.Broken as JsonParser
import Json.Encode
import Parser exposing (Parser)


parseNumberToJsonString : Parser Json.Encode.Value
parseNumberToJsonString =
    Parser.chompWhile Char.isDigit
        |> Parser.getChompedString
        |> Parser.map Json.Encode.string


parseJsonMappingNumbersToStrings : String -> Result Json.Decode.Error Json.Decode.Value
parseJsonMappingNumbersToStrings str =
    let
        (JsonParser.Config defaultConfig) =
            JsonParser.defaultConfig
    in
    str
        |> JsonParser.parseWith (JsonParser.Config { defaultConfig | number = parseNumberToJsonString })
        |> Result.mapError (\err -> Json.Decode.Failure "" Json.Encode.null)


main : Html.Html a
main =
    "{\"fieldName\":4652007308841189719}"
        |> parseJsonMappingNumbersToStrings
        |> Result.andThen (Json.Decode.decodeValue (Json.Decode.field "fieldName" Json.Decode.string))
        |> Result.withDefault "Failed to decode JSON"
        |> Html.text

The integer is more than 2^53 - 1 maxSafeInteger, so it cannot be represented precisely in Elm or JavaScript. 4652007308841189000 is its float representation in JavaScript, losing the precision of the last 3 digits.

Try pasting the number in your browser JavaScript console, you will get the same result.

If you cannot change the JSON format (for example using a string instead of an integer for such numbers), you can either modify the string before decoding it, or use a custom parser, for example using allenap/elm-json-decode-broken, decoding numbers as strings (ex: https://ellie-app.com/7Kmcsgq79yqa1).

3 Likes

Thank you, that solved it!

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.