Search code examples
jsonxmlerlang

How to convert XML strings to JSON in Erlang?


I want to parse XML strings to erlang list and then to JSON.

Example input:

<?xml version="1.0" encoding="UTF-8"?>
<!--some message here-->
<start>
   <data>
      <number id="333">test message</number>
      <data>current date</data>
   </data>
   <mass>
      <client>35</client>
      <address>lattitude</address>
      <code>3454343</code>
      <foo tipo="casa">Some text message 2</foo>
      <product>TEST</product>
   </mass>
</start>

Output should be:

{
  "start": {
    "data": {
      "number": {
        "@id": "333",
        "#text": "test message"
      },
      "data": "current date"
    },
    "mass": {
      "client": "35",
      "address": "lattitude",
      "code": "3454343",
      "foo": {
        "@tipo": "casa",
        "#text": "Some text message 2"
      },
      "product": "TEST"
    }
  }
}

I am trying to use erlsom:simple_form(Xml).

and getting :

{ok,{"start",[],
     [{"data",[],
       [{"number",[{"id","333"}],["test message"]},
        {"data",[],["current date"]}]},
      {"mass",[],
       [{"client",[],["35"]},
        {"address",[],["lattitude"]},
        {"code",[],["3454343"]},
        {"foo",[{"tipo","casa"}],["Some text message 2"]},
        {"product",[],["TEST"]}]}]},
    []}

Now I want to delete these empty attrs. Is there any simple way to do this? thanks in advance.

UPDATE: Make it work w/ solution from Erlang, converting xml to tuples and lists

BUT Getting

{"start",
 [{"data",
   [{"number","test message"},{"data","current date"}]},
  {"mass",
   [{"client","35"},
    {"address","lattitude"},
    {"code","3454343"},
    {"foo","Some text message 2"},
    {"product","TEST"}]}]}

there is no [{"id","333"}] and [{"tipo","casa"}] lists.


Solution

  • The output of your simple parsing is in a set format: {Node, Attributes, Children}, so you can write a simple parser that turns that structure you have into a nested proplist. With that, you can either use mochijson or jsx to turn that proplist into a JSON string.

    -module(transform).
    
    -export([test/0]).
    
    test() -> parse(data()).
    
    parse({Node, [], [Value]}) when is_list(Value) ->
        [{Node, Value}];
    parse({Node, [], Children}) ->
        V = children_to_struct(Children, []),
        [{Node, V}];
    parse({Node, Attributes, Children}) ->
        V = attributes_to_struct(Attributes, []) ++ children_to_struct(Children, []),
        [{Node, V}].
    
    children_to_struct([], Acc) -> Acc;
    children_to_struct([Value], Acc) when is_list(Value) ->
        Acc ++ [{"#text", Value}];
    children_to_struct([Value | T], Acc) when is_tuple(Value) ->
        children_to_struct(T, Acc ++ parse(Value)).
    
    attributes_to_struct([], Acc) -> Acc;
    attributes_to_struct([{K, V}|T], Acc) ->
        attributes_to_struct(T, Acc ++ [{"@" ++ K, V}]).
    
    data() ->
        {"start",[],
         [{"data",[],
           [{"number",[{"id","333"}],["test message"]},
            {"data",[],["current date"]}]},
          {"mass",[],
           [{"client",[],["35"]},
            {"address",[],["lattitude"]},
            {"code",[],["3454343"]},
            {"foo",[{"tipo","casa"}],["Some text message 2"]},
            {"product",[],["TEST"]}]}]}.
    

    Running it in the shell with mochijson:

    Eshell V7.3  (abort with ^G)
    1> c(transform).
    {ok,transform}
    2> T = transform:test().
    [{"start",
      [{"data",
        [{"number",[{"@id","333"},{"#text","test message"}]},
         {"data","current date"}]},
       {"mass",
        [{"client","35"},
         {"address","lattitude"},
         {"code","3454343"},
         {"foo",[{"@tipo","casa"},{"#text","Some text message 2"}]},
         {"product","TEST"}]}]}]
    3> 
    4> iolist_to_binary(mochijson2:encode(T)).
    <<"{\"start\":{\"data\":{\"number\":{\"@id\":[51,51,51],\"#text\":[116,101,115,116,32,109,101,115,115,97,103,101]},\"data\":{\"#text"...>>