Solved

Convert xml to JSON keeping the structure


I just would like to convert xml files to json keeping the structure.

I give you an example of my problem :

 

My xml files look like that :

<Organisme id="pmi-25047-01" codeInsee="25047" dateMiseAJour="2019-04-19" pivotLocal="pmi">

 

<Nom>Centre de protection maternelle et infantile (PMI) - Baume-les-Dames</Nom>

 

<EditeurSource>La Direction de l'information légale et administrative (Premier ministre)</EditeurSource>

 

<Adresse type="géopostale">

 

<Ligne>2 rue des Frères-Grenier</Ligne>

 

<CodePostal>25110</CodePostal>

 

<NomCommune>Baume-les-Dames</NomCommune>

 

<Localisation>

 

<Latitude>47.3492012024</Latitude>

 

<Longitude>6.36126995087</Longitude>

 

<Précision>8</Précision>

 

</Localisation>

 

<Accessibilité type="ACC">rampe d'accès</Accessibilité>

 

</Adresse>

 

<CoordonnéesNum>

 

<Téléphone>03 81 84 48 70</Téléphone>

 

<Télécopie>03 81 84 48 80</Télécopie>

 

<Email>cmsbaumelesdames@doubs.fr</Email>

 

<Url>http://www.doubs.fr</Url>

 

</CoordonnéesNum>

 

<Ouverture>

 

<PlageJ début="vendredi" fin="vendredi">

 

<PlageH début="09:00:00" fin="12:00:00"/>

 

</PlageJ>

 

<PlageJ début="lundi" fin="jeudi">

 

<PlageH début="09:00:00" fin="12:00:00"/>

 

<PlageH début="13:30:00" fin="17:30:00"/>

 

</PlageJ>

 

</Ouverture>

 

</Organisme>

 

What I get when converting it to JSON with FME is that :

 

[

 

{

 

"json_featuretype" : "Organisme",

 

"Adresse.Accessibilité" : "",

 

"Adresse.Accessibilité.type" : "ACC",

 

"Adresse.CodePostal" : 25110,

 

"Adresse.Ligne" : "3 place de la République",

 

"Adresse.Localisation.Latitude" : 47.352582,

 

"Adresse.Localisation.Longitude" : 6.361864,

 

"Adresse.Localisation.Précision" : 8,

 

"Adresse.NomCommune" : "Baume-les-Dames",

 

"Adresse.type" : "géopostale",

 

"CoordonnéesNum.Email" : "msap@baumelesdames.org",

 

"CoordonnéesNum.Téléphone" : "03 81 84 72 45",

 

"CoordonnéesNum.Url" : "https://www.maisondeservicesaupublic.fr/content/doubs-baumois",

 

"EditeurSource" : "La Direction de l'information légale et administrative (Premier ministre)",

 

"Nom" : "France Services (Maison de services au public - MSAP) - Baume-les-Dames",

 

"Organisme.codeInsee" : 25047,

 

"Organisme.dateMiseAJour" : "2020-01-09",

 

"Organisme.id" : "msap-25047-01",

 

"Organisme.pivotLocal" : "msap"

 

}

 

]

 

I tried to work with the lists attributes but the result is still worse.

What I would like my json conversion to look like is that :

{

Organisme: {

 

id: "msap-25047-01",

 

codeInsee: "25047",

 

dateMiseAJour: "2020-01-09",

 

pivotLocal: "msap",

 

Nom: "France Services (Maison de services au public - MSAP) - Baume-les-Dames",

 

EditeurSource: "La Direction de l'information légale et administrative (Premier ministre)",

 

Adresse: {

 

type: "géopostale",

 

Ligne: "3 place de la République",

 

CodePostal: "25110",

 

NomCommune": "Baume-les-Dames",

 

Localisation: {

 

Latitude: "47.352582",

 

Longitude: "6.361864",

 

Précision: "8"

},

 

Accessibilité: { type: "ACC" }

 

},

 

CoordonnéesNum: {

 

Téléphone: "03 81 84 72 45",

 

Email: "msap@baumelesdames.org",

 

Url: "https://www.maisondeservicesaupublic.fr/content/doubs-baumois"

 

},

 

Ouverture: {

 

PlageJ: [

 

{

 

début: "lundi",

 

fin: "mardi",

 

PlageH: [

 

{

 

début: "09:00:00",

 

fin: "12:00:00"

 

},

 

{

 

début: "13:30:00",

 

fin: "17:30:00"

 

}

 

],

 

Note: "Sur rendez-vous le mardi après-midi de 13h30 à 17h30"

 

},

 

{

 

début: "mercredi",

 

fin: "mercredi",

 

PlageH: {

 

début: "09:00:00",

 

fin: "12:00:00"

 

}

 

},

 

{

 

début: "jeudi",

 

fin: "jeudi",

 

PlageH: [

 

{

 

début: "09:00:00",

 

fin: "12:00:00"

 

},

 

{

 

début: "13:30:00",

 

fin: "17:30:00"

 

}

 

],

 

Note: "Permanence à Roulans de 09h à 12h. Permanence à Baume les Dames de 13h30 à 17h30"

 

},

 

{

 

début: "vendredi",

 

fin: "vendredi",

 

PlageH: [

 

{

 

début: "09:00:00",

 

fin: "12:00:00"

 

},

 

{

 

début: "13:30:00",

 

fin: "17:00:00"

 

}

 

]

 

}

 

]

 

}

 

}

 

}

 

I just would like the result to be the same thing that the xml : keep all the attributes and the same structure but written in JSON standards. Is there a simple way to do that with FME ?
icon

Best answer by warrendev 24 January 2020, 01:26

View original

18 replies

Badge +16

Hi @zabe, 

I have done this using the python library xmltodict. It's possible in pure FME, but if you want to go the python route, I have attached an example workspace.

JSON Output

    {
   "Organisme" : {
      "@id" : "pmi-25047-01",
      "@codeInsee" : "25047",
      "@dateMiseAJour" : "2019-04-19",
      "@pivotLocal" : "pmi",
      "Nom" : "Centre de protection maternelle et infantile (PMI) - Baume-les-Dames",
      "EditeurSource" : "La Direction de l'information légale et administrative (Premier ministre)",
      "Adresse" : {
         "@type" : "géopostale",
         "Ligne" : "2 rue des Frères-Grenier",
         "CodePostal" : "25110",
         "NomCommune" : "Baume-les-Dames",
         "Localisation" : {
            "Latitude" : "47.3492012024",
            "Longitude" : "6.36126995087",
            "Précision" : "8"
         },
         "Accessibilité" : {
            "@type" : "ACC",
            "#text" : "rampe d'accès"
         }
      },
      "CoordonnéesNum" : {
         "Téléphone" : "03 81 84 48 70",
         "Télécopie" : "03 81 84 48 80",
         "#text" : "cmsbaumelesdames@doubs.fr\nhttp://www.doubs.fr"
      },
      "Ouverture" : {
         "PlageJ" : [
            {
               "@début" : "vendredi",
               "@fin" : "vendredi",
               "PlageH" : {
                  "@début" : "09:00:00",
                  "@fin" : "12:00:00"
               }
            },
            {
               "@début" : "lundi",
               "@fin" : "jeudi",
               "PlageH" : [
                  {
                     "@début" : "09:00:00",
                     "@fin" : "12:00:00"
                  },
                  {
                     "@début" : "13:30:00",
                     "@fin" : "17:30:00"
                  }
               ]
            }
         ]
      }
   }
}

xml_to_json.fmw

Badge +4

I have an immediate need for converting a complex xml to json while keeping the similar structure as well. Surprised don't see much on the FME Knowledge site. This one https://www.safe.com/convert/xml/json/ claims doing so with no coding, but no detail or examples. I can provide my xml file as an example.

Badge +4

Hi @zabe, 

I have done this using the python library xmltodict. It's possible in pure FME, but if you want to go the python route, I have attached an example workspace.

JSON Output

    {
   "Organisme" : {
      "@id" : "pmi-25047-01",
      "@codeInsee" : "25047",
      "@dateMiseAJour" : "2019-04-19",
      "@pivotLocal" : "pmi",
      "Nom" : "Centre de protection maternelle et infantile (PMI) - Baume-les-Dames",
      "EditeurSource" : "La Direction de l'information légale et administrative (Premier ministre)",
      "Adresse" : {
         "@type" : "géopostale",
         "Ligne" : "2 rue des Frères-Grenier",
         "CodePostal" : "25110",
         "NomCommune" : "Baume-les-Dames",
         "Localisation" : {
            "Latitude" : "47.3492012024",
            "Longitude" : "6.36126995087",
            "Précision" : "8"
         },
         "Accessibilité" : {
            "@type" : "ACC",
            "#text" : "rampe d'accès"
         }
      },
      "CoordonnéesNum" : {
         "Téléphone" : "03 81 84 48 70",
         "Télécopie" : "03 81 84 48 80",
         "#text" : "cmsbaumelesdames@doubs.fr\nhttp://www.doubs.fr"
      },
      "Ouverture" : {
         "PlageJ" : [
            {
               "@début" : "vendredi",
               "@fin" : "vendredi",
               "PlageH" : {
                  "@début" : "09:00:00",
                  "@fin" : "12:00:00"
               }
            },
            {
               "@début" : "lundi",
               "@fin" : "jeudi",
               "PlageH" : [
                  {
                     "@début" : "09:00:00",
                     "@fin" : "12:00:00"
                  },
                  {
                     "@début" : "13:30:00",
                     "@fin" : "17:30:00"
                  }
               ]
            }
         ]
      }
   }
}

xml_to_json.fmw

@warrengis , this works great for me with a relatively complex xml file. Thanks a lot.

Hello I have the same problem but i want to do it in pure FME transformers any idea please it's an emregency?

Thanks in advance for your answer

Hi @zabe, 

I have done this using the python library xmltodict. It's possible in pure FME, but if you want to go the python route, I have attached an example workspace.

JSON Output

    {
   "Organisme" : {
      "@id" : "pmi-25047-01",
      "@codeInsee" : "25047",
      "@dateMiseAJour" : "2019-04-19",
      "@pivotLocal" : "pmi",
      "Nom" : "Centre de protection maternelle et infantile (PMI) - Baume-les-Dames",
      "EditeurSource" : "La Direction de l'information légale et administrative (Premier ministre)",
      "Adresse" : {
         "@type" : "géopostale",
         "Ligne" : "2 rue des Frères-Grenier",
         "CodePostal" : "25110",
         "NomCommune" : "Baume-les-Dames",
         "Localisation" : {
            "Latitude" : "47.3492012024",
            "Longitude" : "6.36126995087",
            "Précision" : "8"
         },
         "Accessibilité" : {
            "@type" : "ACC",
            "#text" : "rampe d'accès"
         }
      },
      "CoordonnéesNum" : {
         "Téléphone" : "03 81 84 48 70",
         "Télécopie" : "03 81 84 48 80",
         "#text" : "cmsbaumelesdames@doubs.fr\nhttp://www.doubs.fr"
      },
      "Ouverture" : {
         "PlageJ" : [
            {
               "@début" : "vendredi",
               "@fin" : "vendredi",
               "PlageH" : {
                  "@début" : "09:00:00",
                  "@fin" : "12:00:00"
               }
            },
            {
               "@début" : "lundi",
               "@fin" : "jeudi",
               "PlageH" : [
                  {
                     "@début" : "09:00:00",
                     "@fin" : "12:00:00"
                  },
                  {
                     "@début" : "13:30:00",
                     "@fin" : "17:30:00"
                  }
               ]
            }
         ]
      }
   }
}

xml_to_json.fmw

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

Userlevel 2
Badge +10

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

@johnmarcdechaud​ I was been able to dig up that workspace for you. I've attached it to this comment.

Userlevel 2
Badge +17

Hello I have the same problem but i want to do it in pure FME transformers any idea please it's an emregency?

Thanks in advance for your answer

Hi @johnmarcdechaud​ , I think using Python would be the easiest way, but you can also apply XQuery solution if Python script isn't allowed to use for some reason.

Assuming the source XML document is stored in a feature attribute called "_xml", execute this expression with XMLTemplater or JSONTemplater.

declare function local:to-json($p as element(), $m as xs:string*) as item()
{
    {|
        for $x in $p/@* return {name($x) : data($x)},
        for $k in distinct-values(for $x in $p/* return name($x))
        return
        {$k : if ($k = $m) then [
                for $x in $p/*[name() = $k]
                return local:to-json($x, $m)
            ]
            else {
                let $x := $p/*[name() = $k]
                return if (exists($x/*)) then local:to-json($x, $m) else data($x)
            }
        }
    |}
};
let $d := fme:get-xml-attribute('_xml')
let $m := distinct-values(
    for $x in $d//*
    where 0 < count($x/following-sibling::*[name() = name($x)])
    return name($x)
)        
return {name($d/*) : local:to-json($d/*, $m)}

 

Thank a lot for your answers I will try the two ways but for the maintenace I prefer doing it in pure FME

Hi @johnmarcdechaud​ , I think using Python would be the easiest way, but you can also apply XQuery solution if Python script isn't allowed to use for some reason.

Assuming the source XML document is stored in a feature attribute called "_xml", execute this expression with XMLTemplater or JSONTemplater.

declare function local:to-json($p as element(), $m as xs:string*) as item()
{
    {|
        for $x in $p/@* return {name($x) : data($x)},
        for $k in distinct-values(for $x in $p/* return name($x))
        return
        {$k : if ($k = $m) then [
                for $x in $p/*[name() = $k]
                return local:to-json($x, $m)
            ]
            else {
                let $x := $p/*[name() = $k]
                return if (exists($x/*)) then local:to-json($x, $m) else data($x)
            }
        }
    |}
};
let $d := fme:get-xml-attribute('_xml')
let $m := distinct-values(
    for $x in $d//*
    where 0 < count($x/following-sibling::*[name() = name($x)])
    return name($x)
)        
return {name($d/*) : local:to-json($d/*, $m)}

 

This functions work even we don't know the structure of the xml???

In the JSONTemplater I have to write you code in ROOT section?

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

Thanx a lot I'll test your FME code

Userlevel 2
Badge +17

Hi @johnmarcdechaud​ , I think using Python would be the easiest way, but you can also apply XQuery solution if Python script isn't allowed to use for some reason.

Assuming the source XML document is stored in a feature attribute called "_xml", execute this expression with XMLTemplater or JSONTemplater.

declare function local:to-json($p as element(), $m as xs:string*) as item()
{
    {|
        for $x in $p/@* return {name($x) : data($x)},
        for $k in distinct-values(for $x in $p/* return name($x))
        return
        {$k : if ($k = $m) then [
                for $x in $p/*[name() = $k]
                return local:to-json($x, $m)
            ]
            else {
                let $x := $p/*[name() = $k]
                return if (exists($x/*)) then local:to-json($x, $m) else data($x)
            }
        }
    |}
};
let $d := fme:get-xml-attribute('_xml')
let $m := distinct-values(
    for $x in $d//*
    where 0 < count($x/following-sibling::*[name() = name($x)])
    return name($x)
)        
return {name($d/*) : local:to-json($d/*, $m)}

 

Yes, Try setting the expression to the ROOT section of the JSONTemplater.

 

Theoretically the expression maps the source XML structure to the destination JSON automatically, but be aware that ​it may not work because it could consume huge memory when the size of source XML was very large. 

In that case, you will have to know the XML structure, and decompose the source XML into small XML fragments with XML reader or XMLFragmenter, convert each XML fragment into JSON fragment with the expression, then aggregate them into a single JSON document with another JSONTemplater.

 

Examle

Source XML Document

<?xml version="1.0" encoding="UTF-8"?>
<Organisme id="pmi-25047-01" codeInsee="25047" dateMiseAJour="2019-04-19" pivotLocal="pmi">
<Nom>Centre de protection maternelle et infantile (PMI) - Baume-les-Dames</Nom>
<EditeurSource>La Direction de l'information légale et administrative (Premier ministre)</EditeurSource>
<Adresse type="géopostale">
<Ligne>2 rue des Frères-Grenier</Ligne>
<CodePostal>25110</CodePostal>
<NomCommune>Baume-les-Dames</NomCommune>
<Localisation>
<Latitude>47.3492012024</Latitude>
<Longitude>6.36126995087</Longitude>
<Précision>8</Précision>
</Localisation>
<Accessibilité type="ACC">rampe d'accès</Accessibilité>
</Adresse>
<CoordonnéesNum>
<Téléphone>03 81 84 48 70</Téléphone>
<Télécopie>03 81 84 48 80</Télécopie>
<Email>cmsbaumelesdames@doubs.fr</Email>
<Url>http://www.doubs.fr</Url>
</CoordonnéesNum>
<Ouverture>
<PlageJ début="vendredi" fin="vendredi">
<PlageH début="09:00:00" fin="12:00:00"></PlageH>
</PlageJ>
<PlageJ début="lundi" fin="jeudi">
<PlageH début="09:00:00" fin="12:00:00"></PlageH>
<PlageH début="13:30:00" fin="17:30:00"></PlageH>
</PlageJ>
</Ouverture>
</Organisme>

 

Resulting JSON document generated by the XQuery expression

{
   "Organisme" : {
      "id" : "pmi-25047-01",
      "codeInsee" : "25047",
      "dateMiseAJour" : "2019-04-19",
      "pivotLocal" : "pmi",
      "Nom" : "Centre de protection maternelle et infantile (PMI) - Baume-les-Dames",
      "EditeurSource" : "La Direction de l'information légale et administrative (Premier ministre)",
      "Adresse" : {
         "type" : "géopostale",
         "Ligne" : "2 rue des Frères-Grenier",
         "CodePostal" : "25110",
         "NomCommune" : "Baume-les-Dames",
         "Localisation" : {
            "Latitude" : "47.3492012024",
            "Longitude" : "6.36126995087",
            "Précision" : "8"
         },
         "Accessibilité" : "rampe d'accès"
      },
      "CoordonnéesNum" : {
         "Téléphone" : "03 81 84 48 70",
         "Télécopie" : "03 81 84 48 80",
         "Email" : "cmsbaumelesdames@doubs.fr",
         "Url" : "http://www.doubs.fr"
      },
      "Ouverture" : {
         "PlageJ" : [
            {
               "début" : "vendredi",
               "fin" : "vendredi",
               "PlageH" : [
                  {
                     "début" : "09:00:00",
                     "fin" : "12:00:00"
                  }
               ]
            },
            {
               "début" : "lundi",
               "fin" : "jeudi",
               "PlageH" : [
                  {
                     "début" : "09:00:00",
                     "fin" : "12:00:00"
                  },
                  {
                     "début" : "13:30:00",
                     "fin" : "17:30:00"
                  }
               ]
            }
         ]
      }
   }
}

 

 

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

Hello,

It's work perfectly I'm not familiar with python code how can I use this code to also convert the xml in csv file dynamically?

thanx in advance.

Hi @johnmarcdechaud​ , I think using Python would be the easiest way, but you can also apply XQuery solution if Python script isn't allowed to use for some reason.

Assuming the source XML document is stored in a feature attribute called "_xml", execute this expression with XMLTemplater or JSONTemplater.

declare function local:to-json($p as element(), $m as xs:string*) as item()
{
    {|
        for $x in $p/@* return {name($x) : data($x)},
        for $k in distinct-values(for $x in $p/* return name($x))
        return
        {$k : if ($k = $m) then [
                for $x in $p/*[name() = $k]
                return local:to-json($x, $m)
            ]
            else {
                let $x := $p/*[name() = $k]
                return if (exists($x/*)) then local:to-json($x, $m) else data($x)
            }
        }
    |}
};
let $d := fme:get-xml-attribute('_xml')
let $m := distinct-values(
    for $x in $d//*
    where 0 < count($x/following-sibling::*[name() = name($x)])
    return name($x)
)        
return {name($d/*) : local:to-json($d/*, $m)}

 

I have an error when running the code you writing

image

Userlevel 2
Badge +17

Hi @johnmarcdechaud​ , I think using Python would be the easiest way, but you can also apply XQuery solution if Python script isn't allowed to use for some reason.

Assuming the source XML document is stored in a feature attribute called "_xml", execute this expression with XMLTemplater or JSONTemplater.

declare function local:to-json($p as element(), $m as xs:string*) as item()
{
    {|
        for $x in $p/@* return {name($x) : data($x)},
        for $k in distinct-values(for $x in $p/* return name($x))
        return
        {$k : if ($k = $m) then [
                for $x in $p/*[name() = $k]
                return local:to-json($x, $m)
            ]
            else {
                let $x := $p/*[name() = $k]
                return if (exists($x/*)) then local:to-json($x, $m) else data($x)
            }
        }
    |}
};
let $d := fme:get-xml-attribute('_xml')
let $m := distinct-values(
    for $x in $d//*
    where 0 < count($x/following-sibling::*[name() = name($x)])
    return name($x)
)        
return {name($d/*) : local:to-json($d/*, $m)}

 

The line 18 is wrong, should be:

let $d := fme:get-xml-attribute('xml')

 

Userlevel 2
Badge +10

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

@johnmarcdechaud​ all credit goes to @Chris Warren​ for that workspace / workaround!

 

Unfortunately the library that this python code uses looks to be specifically for converting XML to JSON. In terms of the XML to CSV workspace, are you looking to read or write dynamically? Or both?

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

hello, i'm looking to convert dynamically a complex XML to CSV I would like the result to be the same thing that the xml structure.

XML Exemple:

<?xml version="1.0" encoding="UTF-8"?>

<marches>

  <marche>

    <id>20212020S0002800</id>

    <acheteur>

      <id>22350001800013</id>

      <nom>Département d'Ille-et-Vilaine </nom>

    </acheteur>

    <nature>Accord-cadre</nature>

    <typeContrat>MARCHE_PUBLIC</typeContrat>

    <objet>FEUILLE DE ROUTE DU NUMERIQUE : ASSISTANCE À MAÎTRISE D'OEUVRE DU SYSTÈME D'INFORMATION DU DÉPARTEMENT D'ILLE-ET-VILAINE - Assistance à maîtrise d'oeuvre : développement logiciels</objet>

    <codeCPV>72000000</codeCPV>

    <procedure>Appel d'offres ouvert</procedure>

    <lieuExecution>

      <code>35</code>

      <typeCode>Code département</typeCode>

      <nom>(35) Ille-et-Vilaine</nom>

    </lieuExecution>

    <dureeMois>48</dureeMois>

    <dateNotification>2021-01-14</dateNotification>

    <datePublicationDonnees>2021-01-14</datePublicationDonnees>

    <montant>2000000</montant>

    <formePrix>Révisable</formePrix>

    <titulaires>

      <titulaire>

        <typeIdentifiant>SIRET</typeIdentifiant>

        <id>35290570700167</id>

        <denominationSociale>AUSY</denominationSociale>

      </titulaire>

<titulaire>

        <typeIdentifiant>SIRET</typeIdentifiant>

        <id>3529057</id>

        <denominationSociale>OBS</denominationSociale>

      </titulaire>

    </titulaires>

    <uuid>41C1567C-86AD-4AA1-AB48-445FC68D18E0</uuid>

  </marche>

</marches>

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

Hello @danminneyatsaf​, @warangis any news from my request?

Userlevel 2
Badge +10

hello i have the same issue can i look up to your .fmw too? Can't find it here.

regards,

@johnmarcdechaud​ while I continue to take a look into this, I'm going to forward you some very useful articles that detail Reading XML dynamically. Hope this helps.

 

https://community.safe.com/s/article/xml-faq-reading-and-writing-xml

https://community.safe.com/s/article/reading-complex-xml-or-gml-using-xfmap

https://community.safe.com/s/article/dynamic-xfmap-example

Reply