Error converting XML to JSON and extracting values

I’m using @billstclair’s XML parser package elm-xml-eeue56 2.0.0

But elm/json doesn’t seem to like it’s output.


I have a simple input:
<summary>\"123\"</summary>

Manually converted to JSON:
{\"summary\":\"123\"}

XML decoder copied from Bill’s first example:

decodeXml : String -> Xml.Value
decodeXml string =
   string
      |> Xml.Decode.decode
      |> Result.toMaybe
      |> Maybe.withDefault Xml.Encode.null

JSON decoder to extract the string:

import Json.Decode as JD

summaryDecoder : JD.Decoder String
summaryDecoder =
   JD.field "summary" JD.string

If I give the decoder the manually converted JSON:
JD.decodeString summaryDecoder "{\"summary\":\"123\"}"
I get the result Ok "123"

However, if I go via Xml:

decodedXml = decodeXml "<summary>\"123\"</summary>"
Debug.log:

Object [Tag "summary" (Dict.fromList []) (StrNode "\"123\"")]

decodedJSON = Xml.xmlToJson2 decodedXml
Debug.log:

<internals>

Check: reEncodedXml = Xml.jsonToXml decodedJSON
Debug.log:

Tag "summary" (Dict.fromList []) (StrNode "\"123\"")

summaryString = JD.decodeValue summaryDecoder decodedJSON
Debug.log:

Err (Failure "Expecting an OBJECT with a field named summary" <internals>)


Notes:

  1. A clue might be in the ‘Check’, where converting back to ‘XML’ does not apparently result in an Object.

  2. Having re-read Bill’s documentation,

The conversion of Tags changed in version 2.0.0, so that they can be properly converted back. The attribute names become JSON keys with “@” prepended, and the value becomes a JSON key named “#value”: <foo x=“x”>1</foo> becomes {foo: {#value: 1, @x: “x”}}

I realized that
<summary>\"123\"</summary>
would instead become
{"summary": {"#value": "123", @x: null}}

So I modified the converter to

summaryDecoder : JD.Decoder String
summaryDecoder =
   JD.field "summary" ( JD.field "#value" JD.string )

But, in the XML to JSON process above, I still get the error

Err (Failure "Expecting an OBJECT with a field named summary" <internals>)

Using this new decoder, but with the ‘corrected’ JSON string given directly:
summaryStrHash = JD.decodeString summaryDecoder "{\"summary\":{\"#value\":\"123\"}}"
Debug.log:

Err (Field "summary" (Failure "Expecting an OBJECT with a field named #value" <internals>))

So here it does now find the ‘summary’ field, but then fails to find the ‘#value’ field.

What am I doing wrong?
Thanks

Xml.xmlToJson2 seems to create a JSON array: [ { "summary": "\"123\"" } ]

I figured this out by first converting the XML to JSON, and then turning that into a string using Json.Encode.encode so I could see what we’ve got to work with.

Play with it here: https://ellie-app.com/cZWvzCV2f8ba1

So here it does now find the ‘summary’ field, but then fails to find the ‘#value’ field.

No, it doesn’t even try to look at the "#value" field since it fails earlier on finding "summary".

1 Like

Thank you, @lydell, that was it!

So,

summaryString = JD.decodeValue summaryDecoder decodedJSON
Err (Failure "Expecting an OBJECT with a field named summary" <internals>)

becomes

summaryList = JD.decodeValue (JD.list summaryDecoder) decodedJSON
Ok ["\"123\""]

<internals> isn’t helpful and I need to debug json value often and actually have a function

debugJson : String -> Json.Encode.Value -> Json.Encode.Value
debugJson string value =
    let
        _ =
            Debug.log string (Json.Encode.encode 0 value)
    in
    value

so I can tag it to any value

decodedJSON =
    Xml.xmlToJson2 decodedXml
        |> debugJson "xmlToJson2"
1 Like

@choonkeat, that’s brilliant. Thank you.

debugJson : String -> JE.Value -> JE.Value
debugJson string value =
   let
      _ = Debug.log string (JE.encode 0 value)
   in
      value
decodedJSON =
   Xml.xmlToJson2 decodedXml
      |> debugJson "decodedJSON"

So,

The debugJson function is given

  • a string to identify the Debug.log output
  • the JSON Value.

The output of the function is just the JSON Value, so the Value passes through; it is unchanged by the function, and therefore doesn’t affect the code where it is inserted.

The clever stuff happens in the let:
The calling of Debug.log generates debug output without assignment. But let must contain assignments. The underscore _ could have been x or y or anything else. But, given that _ is used in case statements to mean ‘any value’, presumably assigning the output of Debug.log to _ is the equivalent of piping the output of a command line function to /dev/null.

Neat :slight_smile:

1 Like

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