How to create a Javascript array or object from an XML file?

203 views Asked by At

I must first state that I am not a champion in programming, although created a handy website using html, php, MySQL and some javascript. Clearly, JS is by far the language I am almost using as a baby !

I have a set of data in a xml file with the following structure (sorry for the bad characters encoding) :

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<markers> 
 <marker lat="50.84310" lng="4.39330" name="7.7cm Sockel Flak 16 RheinMetall" category="FlakKanonen" nation="Allemagne" typecanon="Artillerie de DCA" lieu="Bruxelles - Musée Royal de l'Armée" pays="Belgique" url="../AfficheCanonGET.php?IdCanonAffiche=170" /> 
 <marker lat="43.13810" lng="-80.26170" name="15cm sFH 13 L/14" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu="Brantford (ON) - Monument" pays="Canada" url="../AfficheCanonGET.php?IdCanonAffiche=256" />  
 <marker lat="61.00500" lng="24.45780" name="10cm K 14" category="LeichtKanonen" nation="Allemagne" typecanon="Artillerie légère" lieu="Hameenlinna - Finnish Artillery Museum" pays="Finlande" url="../AfficheCanonGET.php?IdCanonAffiche=317" />  
 <marker lat="45.88740" lng="11.04740" name="7cm M 99 GebK" category="GebirgsKanonen" nation="Autriche-Hongrie" typecanon="Artillerie de montagne" lieu="Rovereto - Museo Storico Italiano della Guerra" pays="Italie" url="../AfficheCanonGET.php?IdCanonAffiche=733" /><marker lat="-35.21550" lng="149.14510" name="13cm K 09 L/35" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu=" Mitchell, ACT - AWM reserve" pays="Australie" url="../AfficheCanonGET.php?IdCanonAffiche=1519" /> 
</markers>

This xml file is used in a javascript code for placing markers on a GoogleMap. Well it used to since the code does not work anymore since GoogleMap migrated to APIV3, without portability of the xml importing function I was using.

I managed to rewrite the map code in the new API v3 language, but I am stuck in what is probably very simple for you : Despite reading tens of similar threads, I am unable to adapt a code in javascript to transfer these xml file data in a javascript array

Here is what I am hoping to create :

marqueurCanonTest4[0] = {
  latitudeCanon: "-34.94570",
  longitudeCanon: "138.58050",
  nomCanon: "7.7cm FK 16",
  categorieCanon: "LeichtKanonen",
  nationCanon: "Allemagne",
  typeCanon: "Artillerie légère",
  lieuCanon: "Adelaide, SA  - Keswick Barracks, Army Museum of South Australia",
  paysCanon: "Australie",
  url: "../AfficheCanonGET.php?IdCanonAffiche=1"
};
marqueurCanonTest4[1] = {
  latitudeCanon: "-36.14640",
  longitudeCanon: "146.91680",
  nomCanon: "7.7cm FK 16",
  categorieCanon: "LeichtKanonen",
  nationCanon: "Turquie",
  typeCanon: "Artillerie légère",
  lieuCanon: "Bandiana, VIC - Army Museum",
  paysCanon: "Australie",
  url: "../AfficheCanonGET.php?IdCanonAffiche=2"
};

etc ...

I have tried to use XMLHttpRequest, but probably incorrectly.

What are ways of achieving my goal?

3

There are 3 answers

5
collab-with-tushar-raj On

If I understood your problem correctly, you need to create a markers array in JavaScript from the provided XML file.

You'll need to parse the XML content and extract the necessary data. You can achieve this using the browser's DOMParser to convert the XML string into a DOM object and then traverse the nodes to extract the marker information.

Here's an example of how you can do this:

// Fetch the XML file
fetch('path/to/your/file.xml')
  .then(response => {
    // Check if the response is successful
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    // Parse the response as text
    return response.text();
  })
  .then(xmlString => {
    // Parse the XML string
    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(xmlString, 'text/xml');

    // Extract marker information
    const markers = Array.from(xmlDoc.getElementsByTagName('marker')).map(marker => ({
      lat: marker.getAttribute('lat'),
      lng: marker.getAttribute('lng'),
      name: marker.getAttribute('name'),
      category: marker.getAttribute('category'),
      nation: marker.getAttribute('nation'),
      typecanon: marker.getAttribute('typecanon'),
      lieu: marker.getAttribute('lieu'),
      pays: marker.getAttribute('pays'),
      url: marker.getAttribute('url'),
    }));

    // Output the markers array
    console.log(markers);

    // Use the 'markers' array in further processing here or within this scope
  })
  .catch(error => {
    console.error('There was a problem fetching the file:', error);
  });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  const mockedXmlResponse = {
    ok: true,
    text: () => new Promise(resolve => 
      resolve(
`<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<markers> 
  <marker lat="50.84310" lng="4.39330" name="7.7cm Sockel Flak 16 RheinMetall" category="FlakKanonen" nation="Allemagne" typecanon="Artillerie de DCA" lieu="Bruxelles - Musée Royal de l'Armée" pays="Belgique" url="../AfficheCanonGET.php?IdCanonAffiche=170" /> 
  <marker lat="43.13810" lng="-80.26170" name="15cm sFH 13 L/14" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu="Brantford (ON) - Monument" pays="Canada" url="../AfficheCanonGET.php?IdCanonAffiche=256" />  
  <marker lat="61.00500" lng="24.45780" name="10cm K 14" category="LeichtKanonen" nation="Allemagne" typecanon="Artillerie légère" lieu="Hameenlinna - Finnish Artillery Museum" pays="Finlande" url="../AfficheCanonGET.php?IdCanonAffiche=317" />  
  <marker lat="45.88740" lng="11.04740" name="7cm M 99 GebK" category="GebirgsKanonen" nation="Autriche-Hongrie" typecanon="Artillerie de montagne" lieu="Rovereto - Museo Storico Italiano della Guerra" pays="Italie" url="../AfficheCanonGET.php?IdCanonAffiche=733" /><marker lat="-35.21550" lng="149.14510" name="13cm K 09 L/35" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu=" Mitchell, ACT - AWM reserve" pays="Australie" url="../AfficheCanonGET.php?IdCanonAffiche=1519" /> 
</markers>`
      )
    ),
  };

  // mocking JavaScript's fetch function.
  function fetch(url) {
    console.log(`fetching ... $${ url }`);

    return new Promise(resolve =>
      setTimeout(resolve, 1500, mockedXmlResponse)
    );
  }
</script>

1
jdweng On

Using Powershell

using assembly System.Xml.Linq 

$filename = 'c:\temp\test.xml'

$doc = [System.Xml.Linq.XDocument]::Load($filename)

$markers = $doc.Descendants('markers')[0]

$table = [System.Collections.ArrayList]::new()
foreach($marker in $markers.Elements())
{
  $newRow = [pscustomobject]@{}
  foreach($attribute in $marker.Attributes())
  {
     $newRow | Add-Member -NotePropertyName $attribute.Name.LocalName -NotePropertyValue $attribute.Value
  }

  $table.Add($newRow) | Out-Null
}
$table | Format-List

Results

lat       : 50.84310
lng       : 4.39330
name      : 7.7cm Sockel Flak 16 RheinMetall
category  : FlakKanonen
nation    : Allemagne
typecanon : Artillerie de DCA
lieu      : Bruxelles - Musée Royal de l'Armée
pays      : Belgique
url       : ../AfficheCanonGET.php?IdCanonAffiche=170

lat       : 43.13810
lng       : -80.26170
name      : 15cm sFH 13 L/14
category  : SchwerenKanonen
nation    : Allemagne
typecanon : Artillerie lourde
lieu      : Brantford (ON) - Monument
pays      : Canada
url       : ../AfficheCanonGET.php?IdCanonAffiche=256

lat       : 61.00500
lng       : 24.45780
name      : 10cm K 14
category  : LeichtKanonen
nation    : Allemagne
typecanon : Artillerie légère
lieu      : Hameenlinna - Finnish Artillery Museum
pays      : Finlande
url       : ../AfficheCanonGET.php?IdCanonAffiche=317

lat       : 45.88740
lng       : 11.04740
name      : 7cm M 99 GebK
category  : GebirgsKanonen
nation    : Autriche-Hongrie
typecanon : Artillerie de montagne
lieu      : Rovereto - Museo Storico Italiano della Guerra
pays      : Italie
url       : ../AfficheCanonGET.php?IdCanonAffiche=733

lat       : -35.21550
lng       : 149.14510
name      : 13cm K 09 L/35
category  : SchwerenKanonen
nation    : Allemagne
typecanon : Artillerie lourde
lieu      :  Mitchell, ACT - AWM reserve
pays      : Australie
url       : ../AfficheCanonGET.php?IdCanonAffiche=1519
3
Peter Seliger On

An approach needs to be based on ...

In addition one could choose to handle the asynchronous API calls by an async main function which makes use of the await operator in order to await the results of such deferred operations.

The beneath provided example code does mock both the fetch method and the method's api response in order to provide a working example of the full data-related round trip.

Choosing reduce for creating and aggregating an new data-item from each currently mapped marker-element is due to gaining flexibility in regards of changes to the XML markup.

While mapping a marker-element one would retrieve a list of all of its attribute-names via getAttributeNames. Parsing the attribute node-list into an array via spread syntax enables the iteration of all attribute-names via reduce. One then can aggregate an initially passed empty object / {}.

With every iteration step one has access to the currently processed attribute-name. Having provided an object as lookup-table allows for a fast lookup of the XML attribute-name's related property-name of the to be aggregated data-item. Thus, with each iteration step one is able of creating a new key-value pair from the correctly looked up key-name.

The solution in its entirety then looks like follows ...

const markerAttrToKeyLookup = {
  lat: 'latitudeCanon',
  lng: 'longitudeCanon',
  name: 'nomCanon',
  category: 'categorieCanon',
  nation: 'nationCanon',
  typecanon: 'typeCanon',
  lieu: 'lieuCanon',
  pays: 'paysCanon',
  url: 'url',
}

function createMarkerListFromXmlDocument(xmlDocument) {
  return [
    ...xmlDocument.querySelectorAll('marker')
  ]
  .map(markerElement => {

    return [...markerElement.getAttributeNames()]
      .reduce((markerItem, attrName) => {

        const key = markerAttrToKeyLookup[attrName];
        
        markerItem[key] = markerElement.getAttribute(attrName);

        return markerItem;

      }, {})
  });
}

async function main() {

  const response = await fetch('path/to/file.xml');
  const xmlText = await response.text();

  console.log({ xmlText });

  const xmlDocument = (new DOMParser)
    .parseFromString(xmlText, 'text/xml');

  // console.log({ xmlDocument });

  const markerList =
    createMarkerListFromXmlDocument(xmlDocument);

  console.log({ markerList });
}
main();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  const mockedXmlResponse = {
    ok: true,
    text: () => new Promise(resolve => 
      resolve(
`<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> 
<markers> 
  <marker lat="50.84310" lng="4.39330" name="7.7cm Sockel Flak 16 RheinMetall" category="FlakKanonen" nation="Allemagne" typecanon="Artillerie de DCA" lieu="Bruxelles - Musée Royal de l'Armée" pays="Belgique" url="../AfficheCanonGET.php?IdCanonAffiche=170" /> 
  <marker lat="43.13810" lng="-80.26170" name="15cm sFH 13 L/14" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu="Brantford (ON) - Monument" pays="Canada" url="../AfficheCanonGET.php?IdCanonAffiche=256" />  
  <marker lat="61.00500" lng="24.45780" name="10cm K 14" category="LeichtKanonen" nation="Allemagne" typecanon="Artillerie légère" lieu="Hameenlinna - Finnish Artillery Museum" pays="Finlande" url="../AfficheCanonGET.php?IdCanonAffiche=317" />  
  <marker lat="45.88740" lng="11.04740" name="7cm M 99 GebK" category="GebirgsKanonen" nation="Autriche-Hongrie" typecanon="Artillerie de montagne" lieu="Rovereto - Museo Storico Italiano della Guerra" pays="Italie" url="../AfficheCanonGET.php?IdCanonAffiche=733" /><marker lat="-35.21550" lng="149.14510" name="13cm K 09 L/35" category="SchwerenKanonen" nation="Allemagne" typecanon="Artillerie lourde" lieu=" Mitchell, ACT - AWM reserve" pays="Australie" url="../AfficheCanonGET.php?IdCanonAffiche=1519" /> 
</markers>`
      )
    ),
  };

  // mocking JavaScript's fetch function.
  function fetch(url) {
    console.log(`fetching ... $${ url }`);

    return new Promise(resolve =>
      setTimeout(resolve, 1500, mockedXmlResponse)
    );
  }
</script>