Reputation: 329
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
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
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
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
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
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
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