I am currently working on a program that takes JSON input and deserializes it using JSON.NET into a dynamic ExpandoObject. Then through a function, I want to dump it out into another textbox to show what it could possibly represent as C# objects. How would I indent my string to show my hierarchical ExpandoObject and take it from a flat list of data to a tree structure of data I can send to a textbox as a string?
Here is some of the code I am using:
if (tbxJSON.Text != "")
{
// Create an ExpandoObjectConverter in order to hold the dynamic parsed JSON.
var converter = new ExpandoObjectConverter();
dynamic convertedJSON = JsonConvert.DeserializeObject<ExpandoObject>(tbxJSON.Text, converter);
tbxCSharp.Text = "";
// Loop through the ExpandoObject and print out all of the values in the dynamic object using a recursive function.
foreach (var property in (IDictionary<string, object>)convertedJSON)
sb.Append(ExpandoToString(property.Value, property.Key));
// Set C# output to contents of StringBuilder.
tbxCSharp.Text = sb.ToString();
}
private string ExpandoToString(object propVal, string propName)
{
string retStr = "";
// If we are dealing with nested JSON
if (propVal.GetType() == typeof(ExpandoObject))
{
// Append this object type.
sb.Append(Indent(indentIdx) + UppercaseFirst(propName) + " " + propName + " consists of..." + Environment.NewLine);
foreach (var prop in (IDictionary<string, object>)propVal)
{
if (prop.Value.GetType() == typeof(ExpandoObject))
sb.Append(ExpandoToString(prop.Value, prop.Key));
else
{
if (prop.Value.GetType() == typeof(List<dynamic>))
{
// TO DO
}
else
sb.Append(ExpandoToString(prop.Value, prop.Key));
}
}
}
else
retStr = propVal.GetType() + " : " + propName + " = " + propVal + Environment.NewLine;
return retStr;
}
Test JSON:
{"airline":"Oceanic","number":815,"departure":{"IATA":"SYD","time":"2004-09-22 14:55","city":"Sydney"},"arrival":{"IATA":"LAX","time":"2004-09-23 10:42","city":"Los Angeles"}}
You should supply your current indent as a parameter to the ExpandoToString() method and increment it any time it calls itself.
private string ExpandoToString(object propVal, string propName, int indent = 0)
{
...
// Append this object type.
sb.Append(Indent(indent) + UppercaseFirst(propName) + " " + propName + " consists of..." + Environment.NewLine);
...
sb.Append(ExpandoToString(propVal, propName, indent + 1) ...);
Related
Using the example code works correctly to get the non-nested data from "issues", how can I get the nested data from "issues"? In the picture you can see which data we are talking about.
Code:
try
{
var dynObj = JsonConvert.DeserializeObject<dynamic>(strResponseValue);
string records = dynObj["issues"].ToString();
JArray a = JArray.Parse(records);
List<string> colNames = new List<string>() {"expand", "key", "id",
"self" };
string headerRow = "";
foreach (string colName in colNames)
{
headerRow += "\"" + colName + "\";";
}
headerRow = headerRow.TrimEnd(';');
headerRow += "\n";
string dataRows = "";
foreach (var record in a)
{
string thisRecord = "";
foreach (string colName in colNames)
{
thisRecord += "\"" + record[colName] + "\";";
}
thisRecord = thisRecord.TrimEnd(';');
thisRecord += "\n";
dataRows += thisRecord;
}
csvData = headerRow + dataRows;
Console.WriteLine("\ncsvData:");
Console.WriteLine(csvData);
}
catch (Exception ex)
{
Console.WriteLine("Exeption Error" + ex.ToString());
}
If the json is complex, you can let visual studio do the job.
Copy the json content
Use Edit/Paste Special - Paste JSON As Classes
Use the generated Class to deserialize your json content.
For instance deserialize to MyClass (from .Net Fundumentals - JSON Serialize/Deserialize):
MyClass? myclass = JsonSerializer.Deserialize<MyClass>(jsonString);
if (GUILayout.Button("Copy settings"))
{
var selection = Selection.gameObjects.ToList();
for (int i = selection.Count - 1; i >= 0; --i)
{
var selected = selection[i];
WriteDataToFile("Transform " + i.ToString() + " Name ==> " + selected.name);
WriteDataToFile("************************" +
"********************");
if (selected.transform.parent != null)
WriteDataToFile(selected.transform.parent.ToString());
WriteDataToFile("local position " + selected.transform.localPosition.ToString());
WriteDataToFile("local rotation " + selected.transform.localRotation.ToString());
WriteDataToFile("local scale " + selected.transform.localScale.ToString());
WriteDataToFile("************************" +
"********************");
WriteDataToFile(" ");
}
}
And the WriteDataToFile:
private void WriteDataToFile(string line)
{
string path = "Assets/Resources/test.txt";
StreamWriter writer = new StreamWriter(path, true);
writer.WriteLine(line);
writer.Close();
}
First I want to check that if I click more then once on the button and it's the same data: name position rotation scale don't write again.
Second how can I read back the lines of the data and assign them into a object ? Also the name. So a new object will be created in the same name parent if the original was parent position rotation and scale.
This is how I'm reading now:
void ReadString()
{
string path = "Assets/Resources/test.txt";
StreamReader reader = new StreamReader(path);
Debug.Log(reader.ReadToEnd());
reader.Close();
}
To add new data to the file that doesn't already exist:
private void WriteDataToFile(string line)
{
string path = "Assets/Resources/test.txt";
string[] text = new string[];
if(File.Exists(path))
{
text = File.ReadAllLines(path);
if(!text.Contains(line))
File.AppendAllText(path, Line);
}
}
If you are not limited to using a text file for storing and retrieving data then I recommend finding a way to write all this data:
WriteDataToFile("Transform " + i.ToString() + " Name ==> " + selected.name);
WriteDataToFile("************************" +
"********************");
if (selected.transform.parent != null)
WriteDataToFile(selected.transform.parent.ToString());
WriteDataToFile("local position " + selected.transform.localPosition.ToString());
WriteDataToFile("local rotation " + selected.transform.localRotation.ToString());
WriteDataToFile("local scale " + selected.transform.localScale.ToString());
WriteDataToFile("************************" +
"********************");
WriteDataToFile(" ");
in less writes because opening and closing the file could be expensive. Maybe something like this:
var selected = selection[i].transform;
string toWrite = $"{parent}:{localPosition}:{localRotation}:{localScale}";
WriteDataToFile(toWrite);
This would mean retrieval would be simply - (not sure the type)
private gameObject GetObjectFromFile(Path, Id)
{
string[] text = new string[];
if(File.Exists(path))
{
text = File.ReadAllLines(path);
foreach(string s in text)
{
if(s.Split(':')[0] == Id.ToString())
{
text = s.Split(':');
break;
}
}
var Id = Convert.ToInt32(text[0]);
var localPosition = Convert.ToInt32(text[1]);
var localRotation = Convert.ToInt32(text[2]);
var localScale = Convert.ToInt32(text[3]);
return new gameObject(Id, localPosition, localRotation, localScale);
}
I am trying to loop over an object properties and values and build a string with them.
The problem is i cant seem to access the values of the properties which are not string...
Here is what i have so far:
private string ObjectToStringArray(CustomType initParameters)
{
var stringArray = "";
foreach (var parameter in initParameters.GetType().GetProperties())
{
if (parameter.PropertyType.Name == "String")
stringArray += "\"" + parameter.Name + "\" => \"" + parameter.GetValue(initParameters) + "\",\r\n";
else
{
stringArray += "array(\r\n";
foreach (var subParameter in parameter.PropertyType.GetProperties())
{
stringArray += "\"" + subParameter.Name + "\" => \"" + subParameter.GetValue(parameter) + "\",\r\n";
}
stringArray += "),";
}
}
return stringArray;
}
i can get to the values of all the string properties but one level down i just cant extract the property object itself.
My exception is: System.Reflection.TargetException: Object does not match target type.
When calling subParameter.GetValue(parameter), you are passing a PropertyInfo, whereas you seemingly want to pass the value of that property for initParameters instead.
You should thus pass parameter.GetValue(initParameters) to subParameter.GetValue() instead.
I was trying to retrieve data from Amazon SimpleDB and currently it only displays data in text like domainName: {attribute1, value2} {attribute1, value2}.
How can I show the data in data grid view? My code is as follows:
public static List<String> GetItemByQuery(IAmazonSimpleDB simpleDBClient, string domainName)
{
List<String> Results = new List<String>(); ;
SelectResponse response = simpleDBClient.Select(new SelectRequest()
{
SelectExpression = "Select * from " + domainName
});
String res = domainName + " has: ";
foreach (Item item in response.Items)
{
res = item.Name + ": ";
foreach (Amazon.SimpleDB.Model.Attribute attribute in item.Attributes)
{
res += "{" + attribute.Name + ", " + attribute.Value + "}, ";
}
res = res.Remove(res.Length - 2);
Results.Add(res);
}
return Results;
}
How you an read here:
http://docs.aws.amazon.com/sdkfornet1/latest/apidocs/html/P_Amazon_SimpleDB_Model_SelectResult_Item.htm
your response.Items is
public List<Item> Item { get; set; }
so you should directly use to DataSource of your Grid, set autogenerate column to your grid to start to view the result
Based on the example c# dynamic with XML, I modified DynamicXml.cs and parsed my xml string.
the modified part is as follows
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (binder.Name == "Controls")
result = new DynamicXml(_elements.Elements());
else if (binder.Name == "Count")
result = _elements.Count;
else
{
var attr = _elements[0].Attribute(
XName.Get(binder.Name));
if (attr != null)
result = attr.Value;
else
{
var items = _elements.Descendants(
XName.Get(binder.Name));
if (items == null || items.Count() == 0)
return false;
result = new DynamicXml(items);
}
}
return true;
}
The xml string to parse:
"< View runat='server' Name='Doc111'>" +
"< Caption Name='Document.ConvertToPdf' Value='Allow Conversion to PDF'></ Caption>" +
"< Field For='Document.ConvertToPdf' ReadOnly='False' DisplayAs='checkbox' EditAs='checkbox'></ Field>" +
"< Field For='Document.Abstract' ReadOnly='False' DisplayAs='label' EditAs='textinput'></ Field>" +
"< Field For='Document.FileName' ReadOnly='False' DisplayAs='label' EditAs='textinput'></ Field>" +
"< Field For='Document.KeyWords' ReadOnly='False' DisplayAs='label' EditAs='textinput'></ Field>" +
"< FormButtons SaveCaption='Save' CancelCaption='Cancel'></ FormButtons>" +
"</ View>";
dynamic form = new DynamicXml(markup_fieldsOnly);
is there a way to serialize the content of this dynamic object(name value pairs inside dynamic) form as JSON object and sent to client side(browser)?
I've heard Json.Net works pretty well, though I've never used it myself.