hoek rand
hoek rand

Reputation: 329

Having trouble with deserializing a JSON String

for a project I'm working on at the moment I'm experiencing alot of trouble. I get a JSON-string from an external source. The JSON-string I'm receiving is as follows:

{
    "PC_Station": [{
        "PLC_0": {
            "DB1": {
                "test123": 0
            },
            "STOP": false,
            "START": false,
            "Start_1": false,
            "Stop_1": false,
            "Led1": true,
            "Led2": false,
            "Led3": true,
            "Counter": 4002,
            "Sliderval": 0
        }
    }, {
        "PLC_1": {
            "DB1": {
                "test123": 0
            },
            "Led_1": false,
            "Led_2": false,
            "Led_3": false,
            "Led_4": false,
            "Tag1": true,
            "Tag2": false,
            "Tag3": true,
            "Counter": 4002,
            "randomNr": 0
        }
    }]
}

The external source is made in a way that a PLC device(Industrial I/O) sends all the variables that it has to a server. The server collects the name of the PLC device along with all the variables that it contains and adds all PLC devices to an array, like in the JSON above.

What I need: I'm trying to make a JSON Deserializer in C# that dynamically catches all variables and variable names. I'm going to use this in an application I'm making for an assignment. It's to populate a GUI with PLC variables in Unity3d, but that's unrelated in this context.

The last thing that i tried was this:

static void Main(string[] args)
        {
            string json = "{\"PC_Station\": [{\"PLC_0\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}},{\"PLC_1\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}}]}";
            JObject root = JObject.Parse(json);
            dynamic pcstation = root["PC_Station"];
            for(int x = 0; x < pcstation.Count; x++)
            {
                Console.WriteLine(pcstation[x]);
            }
            Console.ReadLine();
        }

When printing just the x in the for loop, I'm getting 0 and 1 as output, meaning there are 2 items, aka the two PLC devices in the array under PC_station. I feel like I'm almost there.

I hope anyone can help me figure out what I need to do because I'm almost at my wits end.

EDIT 1: Seems I wasn't very clear what I want, you see, The example JSON-code I received is from two random PLC devices. Every PLC has their own variables, hence the reason I can't use classes that are generated by json2csharp. I want to dynamically deserialize and use the variables that I received from the server to visualize them in Unity.

Upvotes: 0

Views: 301

Answers (6)

hoek rand
hoek rand

Reputation: 329

Managed to fix my problem! Used information you guys shared and made a god awful pile of code. But hey it works :P. Here's the code I came up with using the suggestions posted here:

using Newtonsoft.Json.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UI;

public class generateUI : MonoBehaviour
{
    public static int size = 0;
    public static List<Dictionary<string, string>> abc = new List<Dictionary<string, string>>();
    public GameObject canvas;
    public GameObject Panel;
    public GameObject image;
    public GameObject imagetext;
    private float scaler = 0.0125f;
    void Start()
    {
        string json = "{\"PC_Station\": [{\"PLC_0\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}}]}";
        //string json = "{\"PC_Station\": [{\"PLC_0\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}},{\"PLC_1\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}}]}";
        Panel.transform.SetParent(canvas.transform, false);
        var root = JToken.Parse(json);
        IterateJtoken(root);
        List<string> varz = new List<string>();
        foreach(var item in abc)
        {
            foreach(var it in item)
            {
                varz.Add(it.Key);
            }
        }




        GameObject[] tiles = new GameObject[size];
        GameObject[] texts = new GameObject[size];
        int tilenum = 0;
        for (int i = 0; i < size; i++)
        {
            tilenum++;
            tiles[i] = Instantiate(image, transform.position, transform.rotation);
            tiles[i].name = "tile"+tilenum;
            tiles[i].transform.SetParent(Panel.transform, false);
            texts[i] = Instantiate(imagetext, transform.position, transform.rotation);
            texts[i].transform.SetParent(tiles[i].transform, false);
            texts[i].GetComponent<Text>().text = varz[i];
            texts[i].transform.position += new Vector3(55*scaler, -4*scaler, 0);
        }
    }

    public static void test()
    {
        int i = 0;
        foreach(var item in abc)
        {
            foreach(var it in item)
            {
                i++;
            }
        }
        Debug.Log(i);
    }

    public static void IterateJtoken(JToken jtoken)
    {
        foreach (var value in jtoken)
        {
            foreach (JArray test in value)
            {
                for (int i = 0; i < test.Count; i++)
                {
                    foreach (var item in test[i])
                    {
                        var itemproperties = item.Parent;
                        foreach (JToken token in itemproperties)
                        {
                            if (token is JProperty)
                            {
                                var prop = token as JProperty;
                                //Console.WriteLine(prop.Name);           //PLC name
                                var plc = (JObject)prop.Value;
                                Dictionary<string, string> variables = new Dictionary<string, string>();
                                foreach (KeyValuePair<string, JToken> val in plc)
                                {

                                    if (val.Value is JObject)
                                    {
                                        JObject nestedobj = (JObject)val.Value;
                                        foreach (JProperty nestedvariables in nestedobj.Properties())
                                        {
                                            size++;
                                            var nestedVariableName = nestedvariables.Name;
                                            var nestedVariableValue = nestedvariables.Value;
                                            variables.Add(nestedVariableName, nestedVariableValue.ToString());
                                            //Console.WriteLine(nestedVariableName+" "+nestedVariableValue);
                                        }

                                    }
                                    else
                                    {
                                        size++;
                                        var variableName = val.Key;
                                        var variableValue = val.Value;
                                        variables.Add(variableName, variableValue.ToString());
                                        //Console.WriteLine(variableName+" "+variableValue);
                                    }

                                }
                                abc.Add(new Dictionary<string, string>(variables));
                            }
                        }
                    }
                }
            }


        }

    }
}

This is the script I attach to an empty gameobject in Unity3D. I also made a Canvas(width:1090, height:430) with a Panel Child. I add to this a grid layout group with a cellsize of 100x100 and spacing of 10x10. The Canvas and panel will be dragged to the script attached to the empty gameobject. After dragging those to the script, you'll need to make two prefabs:

  • An UI/Image of 100x100, color white

  • An UI/Text (160 width, 30 height)

After creating those two, drag them to the script attached to the empty gameobject. Now, when you launch the Unity3d application, you'll see a canvas get populated by all the PLC variables available in the JSON string.

Switch the 2 strings to see it dynamically change at runtime(first string will add 10 elements while the second will add 20 elements).

If there are any suggestions on cleaning up the code, please do tell.

Upvotes: 0

Prany
Prany

Reputation: 2143

You can use Newtonsoft.json

string line = "{\"PC_Station\": [{\"PLC_0\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}},{\"PLC_1\": {\"DB1\": {\"test123\": 0}, \"STOP\": false, \"START\": false, \"Start_1\": false, \"Stop_1\": false, \"Led1\": true, \"Led2\": false, \"Led3\": true, \"Counter\": 4002, \"Sliderval\": 0}}]}";
var files = JObject.Parse(line);
var recList = files.SelectToken("$..PC_Station").ToList();

    foreach (var item in recList)

    {
        for (int i = 0; i < item.Count(); i++)
        {
            Console.WriteLine(recList[i]);
        }
    }

Upvotes: 1

T McKeown
T McKeown

Reputation: 12857

I think I understand your issue. If you are not able to define a C# class that can be used to map your JSON to C# then you need to rethink it. Why not create a simple C# class with nothing more than a Dictionary<string,string> or HashSet<string,string>

If you are the one designing how the payload from the PLC is constructed then you can make it fit a simple C# class that has some basic properties and a dictionary for the variables that are not consistently created/available.

or if you want dynamic type behavior, https://weblog.west-wind.com/posts/2012/Aug/30/Using-JSONNET-for-dynamic-JSON-parsing

Upvotes: 1

Devanshu
Devanshu

Reputation: 933

JSON.parse convert json to  object.

Accessing Object Values. You can access the object values by using dot (.)

 for(int x = 0; x < pcstation.Count; x++)
            {
                Console.WriteLine(pcstation[x].PLC_0.DB1.test123);//0
                Console.WriteLine(pcstation[x].STOP);//false

 }

Upvotes: 1

Terry Lennox
Terry Lennox

Reputation: 30685

You can parse this JSON using JToken, this makes it easy to iterate over properties and access objects without necessarily knowing the entire structure beforehand.

var json = @"{
""PC_Station"": [{
        ""PLC_0"": {
            ""DB1"": {
                ""test123"": 0
            },
            ""STOP"": false,
            ""START"": false,
            ""Start_1"": false,
            ""Stop_1"": false,
            ""Led1"": true,
            ""Led2"": false,
            ""Led3"": true,
            ""Counter"": 4002,
            ""Sliderval"": 0
        }
    }, {
        ""PLC_1"": {
            ""DB1"": {
                ""test123"": 0
            },
            ""Led_1"": false,
            ""Led_2"": false,
            ""Led_3"": false,
            ""Led_4"": false,
            ""Tag1"": true,
            ""Tag2"": false,
            ""Tag3"": true,
            ""Counter"": 4002,
            ""randomNr"": 0
        }
    }]
}";


var root = JToken.Parse(json);
int i = 0;
foreach (var item in root["PC_Station"].Values())
{
    Console.WriteLine("Item {0}: {1}", i++, item);
}

You can easily enumerate properties of the root JToken object, e.g.

static void TraverseJToken(JToken jtoken)
{
    foreach (var value in jtoken.Values())
    {
        if (value.HasValues)
        {
            TraverseJToken(value);
        }
        else
        {
            Console.WriteLine(value.Path + ": " + value.ToObject<string>());
        }
    }
}

TraverseJToken(root);

You can also select individual values and subsets of the JSON tree:

var Counter = root.SelectToken("PC_Station[0].PLC_0.Counter").Value<int>();
Console.WriteLine("Counter: " + Counter);

Upvotes: 1

Manish Anjara
Manish Anjara

Reputation: 51

try to convert two array items into list, that will do the trick. I have faced same problem and it was resolved with same. Try, if it doesn't work let me know. I will share code with you.

Upvotes: 1

Related Questions