parsing values from json into list or dictionary

As a beginner I am struggling to find a starting point with my problem:
I am retrieving data in json format through API calls. It is geo data of a given location and the result looks something like this:

{
  "version": 0.6,
  "generator": "Overpass API 0.7.59.4 36d058c8",
  "osm3s": {
    "timestamp_osm_base": "2023-03-31T20:39:54Z",
    "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
  },
  "elements": [
{
  "type": "node",
  "tags": {
    "amenity": "parking_space",
    "capacity": "1",
    "parking_space": "disabled"
  }
},
{
  "type": "way",
  "tags": {
    "electrified": "contact_line",
    "frequency": "50",
    "gauge": "1435",
    "maxspeed": "90",
    "operator": "DB",
    "passenger_lines": "2",
    "railway": "rail",
    "railway:etcs": "1",
    "ref": "1",
    "tracks": "2",
    "usage": "main",
    "voltage": "25000"
  }
},
{
  "type": "way",
  "tags": {
    "building": "house",
    "source": "bing"
  }
},
{
  "type": "way",
  "tags": {
    "landuse": "residential",
    "name": "Pfaffental"
  }
},
{
  "type": "way",
  "tags": {
    "access": "private",
    "leisure": "garden"
  }
},
{
  "type": "way",
  "tags": {
    "landuse": "railway",
    "layer": "1",
    "man_made": "bridge"
  }
},
{
  "type": "way",
  "tags": {
    "natural": "cliff"
  }
},

Now I want to parse all values of all tags into a single list of strings (“parking_space”, “1”, “disabled”, …) or alternatively a dictionary with key:value pairs (“amenity”:“parking_space”, “capacity”: “1”, …) in order to search for specific keywords from the results.

I’ve been researching far and wide how to correctly parse/deserialize json in unity and almost always the answer was to create an object. Considering the format I get the data in, I would get multiple objects (depending on the API response can be over 50) with each a list/dictionary of existing tags and thats quite complicated to handle. Is there a better solution to this if I don’t need any other data than the values of the tags from the API response?

I also read that the Unity Json.Utility cannot parse into a dictionary so tried to find Info on how to do it with Json.NET but couldnt figure it out. Besides, if I understood correctly a dictionary must have unique keys, so with duplicates like eg. “landuse”: “residential” and “landuse”: “railway” it will not work, right? How can i parse the values of the tags into a list instead?

I am very grateful for any suggestion or explanation on how to extract the needed data!

Well, Json.NET should be able to simply parse your json into a dictionary / array structure. However since json allows any json value be a value in those arrays / dictionaries, those datatypes are usually untyped and only contain “objects”. So you need a lot of type checking / casting in order to use the data.

I’ve written my [SimpleJSON parser][1] which is just a single C# file which parses the data into actual classes that represent the possible json values (JSONString, JSONNumber, JSONBool, JSONObject, JSONArray, JSONNull). Those are specifically designed to make handling the data easy. You barely have to think about the individual subtypes and most the time you just work with the base class JSONNode. It can be implicitly converted into a string, int, float, double, bool to actually read the values. The JSONNode also has to indexers which allow you to read arrays and objects by using either an index or a string key. It also provides an enumerator so you can simply iterate over the key value pairs of an object or the elements of an array.

Assuming you have your json already loaded in a string variable, you can simply do

JSONNode root = JSON.Parse(yourJsonString);

To access the elements you can do

foreach(JSONNode el in root["elements"])
{
    Debug.Log("Element type: " + el["type"].Value);
    foreach(var kv in el["tags"])
    {
        Debug.Log(kv.Key + " = " + kv.Value);
    }
}

Knowing a certain path you can also directly do

string freq = root["elements"][2]["tags"]["frequency"];

Though since the tags seems to be quite random, iterating through them with a foreach loop probably makes more sense.

Note that technically the foreach loop would always loop over a KeyValuePair<string, JSONNode>. Though I implemented an implicit conversion from that key value pair to just JSONNode. That’s why the first foreach loop actually works. Instead of a foreach loop you can also use a for loop, at least for arrays, not for objects. A JSONNode has a Count property, so you can just do

var elements = root["elements"];
for(int i = 0; i < elements.Count; i++)
{
    string type = elements*["type"];*

// [ … ]
Finally the whole framework is build using only partial classes. This makes it easy to extend. I have a [Unity specific extension file][3] that just needs to sit next to the SimpleJSON.cs. It adds conversion support for Vector3 and some other Unity types. So an object like
{
“x”: 42, “y”: 123, “z”: -5
}
can directly be converted into a Vector3. The same is true for an array like this [42, 123, -5]. You probably don’t need this here, but I just wanted to mention it :slight_smile:

Hi @avi_design Yes, a class object would be the most direct means of performing the load/parse. Most likely, there’s a class design paradigm which would encompass all the types of data you’d receive, with some of the data just left empty. You could roll your own parser, but as the expression goes, there’s no need to re-invent the wheel.

And you are correct that a dictionary requires unique keys. The obvious solution would be for the dictionary values to be arrays, instead of just a string value. But, given that the data contains multiple entries of type, tag, and many more, the stuffing of this data into a dictionary would be a little messy.

According to the openstreetmap wiki, there are some wrapper libraries for using the data from the OSM API. Also, on the software libraries page, there is a C# wrapper called OSMJSON.Net which I suspect will accomplish what you want.