Load multiple 'nodes' from JSON and store into array - c#

I am currently creating a small Text-Based Game. In this there are obviously multiple rooms, I wish to load those rooms from a JSON file. I am currently doing that as such:
dynamic jRooms = Json.Decode(file);
for (int i = 0; i < Regex.Matches( file, "Room" ).Count; i++){
name[i] = jRooms.Game.Room[i];
description[i] = jRooms.Game.Room.Attributes.Description[i];
exits[i] = jRooms.Game.Room.Attributes.Exits[i];
_count++;
}
That loads information from the following JSON file:
{
'Game': [{
'Room': 'Vault 111 Freeze Chamber',
'Attributes': {
'Description': 'The freeze chamber of the vault you entered after the nuclear fallout.',
'Exits': 'North.Vault 111: Main Hallway'
},
'Room': 'Vault 111 Main Hallway',
'Attributes': {
'Description': 'The main hallway of the vault.',
'Exits': 'South.Vault 111: Freeze Chamber'
}
}]}
This unfortunately throws up an error during run time that I can't seem to work out, which is the following:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot perform runtime binding on a null reference
at CallSite.Target(Closure , CallSite , Object , Int32 )
at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)
at TBA.Loader.Rooms()
at TBA.Program.Main(String[] args)
Any help would be greatly appreciated, because I am completely stumped as to what is wrong and not working. If you need anymore of my code, just request it.
Thanks.

The problem is with your JSON. JSON doesn't allow single quotes (maybe they have a different meaning or no meaning at all). Source - W3Schools.
Use services like JSONLint to validate JSON and check for errors. Even JSONLint declares your JSON, invalid. Using double quotes however, it is declared valid. You should use double quotes like this:
{
"Game": [
{
"Room": "Vault111FreezeChamber",
"Attributes": {
"Description": "Thefreezechamberofthevaultyouenteredafterthenuclearfallout.",
"Exits": "North.Vault111: MainHallway"
},
"Room": "Vault111MainHallway",
"Attributes": {
"Description": "Themainhallwayofthevault.",
"Exits": "South.Vault111: FreezeChamber"
}
}
]
}

Related

Deserializing JSON using UnityEngine.JsonUtility returns null list

I am trying to mod a Unity game using BepInEx. I want to read a list of a type of class I've created from a JSON, then read each object from that class. I used UnityEngine.JsonUtility to do this and created a wrapper class containing my list.
[System.Serializable]
public class StarSignDataList
{
public List<StarSignData> starSigns;
}
// Stores all data pertaining to star signs as read from JSON file.
[System.Serializable]
public class StarSignData
{
public string name;
public int id;
public string description;
}
Then I read the json file from the correct file path, parsing that information into a string and called the JsonUtility.FromJason() method to convert the string into my StarSignDataList class.
// Reads all star sign data from JSON and assigns values to list.
public static void InitializeData()
{
log.LogInfo("Initializing Star Sign data.");
string json = File.ReadAllText("BepInEx/plugins/StarSigns/starsigns.json");
log.LogInfo(json);
StarSignDataList list = UnityEngine.JsonUtility.FromJson<StarSignDataList>(json);
log.LogWarning(list.starSigns.Count == 0 ? "Not null" : "Null!");
log.LogInfo(list.starSigns[0].name);
signs = list.starSigns;
foreach (StarSignData data in signs)
{
log.LogInfo(data.name);
}
}
When I print out the contents of my json immediately after reading it, it comes out as expected, looking exactly like my json file. However when I convert the json string into my list class, the list is completely empty and then throws a NullReferenceException when trying to print the first object. I've trying using arrays instead of lists, I've checked that my json is correctly written, I've pored over countless google search results without success.
Here is my json file.
{
"starSigns": [
{
"name": "Sign of the Fool",
"id": 0,
"description": "You are of normal birth."
},
{
"name": "Sign of Loki",
"id": 1,
"description": "The sign of the god of betrayal shone above your birth place."
},
{
"name": "Sign of Odin",
"id": 2,
"description": "A tumultuous child at birth, your muscles bear the strength of Odin himself."
}
]
}
Out of desperation I tried converting a class like this to a json, and it just wrote to the file "{ }". When I try to do the same with just my StarSignData class it works as expected, but I need to put multiple objects in my json and the list class does not seem to work. I also can't use external libraries such as SimpleJson for this.

Parsing InfluxDB result using JSON.net

I'm trying to make a command line utility for initializing a InfluxDB database, but I'm pretty new to influx, and C# in general.
With the following response from the Influx DB Database, I'm trying to pretty print this in the console window.
Ideally I would have errors show up in the standard error buffer, and warnings or info's show up in the standard output.
However, when running the code below in a debug environment, messages appears to be in an incorrect format according to several jsonpath checkers that I have used.
JSON input as result.Body
{
"results": [
{
"statement_id": 0,
"messages": [
{
"level": "warning",
"text": "deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}
]
}
]
}
JSON output as messages prior to transformation:
messages {{
"level": "warning",
"text": "deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}} Newtonsoft.Json.Linq.JToken {Newtonsoft.Json.Linq.JObject}
As you can see, the messages output is in a nested object {{}} rather then an array as expected...
According to https://jsonpath.curiousconcept.com/ and several other jsonpath checkers, I was expecting something similar to:
[
{
"level":"warning",
"text":"deprecated use of 'CREATE RETENTION POLICY Primary ON SensorData DURATION 30d REPLICATION 1' in a read only context, please use a POST request instead"
}
]
C#
private static void PrintResult(IInfluxDataApiResponse result)
{
var output = result.Success ? System.Console.Out : System.Console.Error;
output.WriteLine("["+result.StatusCode + "] : "+result.Body);
var json = JObject.Parse(result.Body);
var messages = json.SelectToken("$.results[*].messages[*]"); //outputs an array of messages if exists. e.g. [{level:warning,text:test}]
if (messages != null)
{
var transformed = messages.Select(m => new { level = (string)m["level"], text = (string)m["text]"] }).ToList();
foreach (var msg in transformed)
{
output.WriteLine($"[{result.StatusCode}] : {msg.level} - {msg.text}");
}
}
}
For my uses at least, using var messages =
json.SelectTokens("$.results[*].messages[*]");
rather then
json.SelectToken("$.results[*].messages[*]");
allowed me to workaround the issue, as I could then treat the result as a C# enumerable, as opposed to special casing 1 result vs many results for SelectToken as it seems to flatten single results into an object, where as other implementations would have it be an array.

Ez.Newsletter.MagentoApi product_attribute.addOption

I have found the Ez.Newsletter.MagentoApi C# project on the internet.
I think its a great tool to Test the Magento SOAP API.
But after struggling with some code for WEEKS now I decided to ask a question.
In the project, there is no sample for the AddOption in the ProductAttributeOption (Link).
This is the Public Method I have added to the Api solution:
public static bool addOption(string apiUrl, string sessionId, object[] args)
{
IProductAttributeOption prox = (IProductAttributeOption)XmlRpcProxyGen.Create(typeof(IProductAttributeOption));
prox.Url = apiUrl;
return prox.addOption(sessionId, _catalog_product_attribute_add_option, args);
}
And this is the code for adding the Option:
bool OptionAdded = ProductAttributeOption.addOption(apiUrl, sessionId, new object[] {
attributeCode,
new object[] {
new object[] {
"0", //store_id
"New Label Name" //value
},
"0", //orderid
"0" //is_default
}
});
But the error of the server is like:
An unhandled exception of type 'CookComputing.XmlRpc.XmlRpcFaultException' occurred in CookComputing.XmlRpcV2.dll
Additional information: Server returned a fault exception: [108] Default option value is not defined
I just came across this issue myself. We use Python with Magento1.9 Xml-RPC
You're currently formatting very similar as I did initially:
{'label': {'store_id': '0','value':'Purple'}, 'is_default': 0, 'order': 0}
After some playing around, wrapping the label value in another list did the trick:
{'label': [{'store_id': '0','value':'Purple'}], 'is_default': 0, 'order': 0}
This is my 5cents. Hope it helps you forward.

C# .NET: Deserialize json tree

Good evening,
For my specific problem, I have to create a menu (a tree of menus more exactly). Thus, I decided to use Composite Design Pattern with the following structure:
IMenuComponent (an interface defining some properties and so on)
Menu (contains a list of IMenuComponent)
MenuEntry (a leaf)
So, I'll have to navigate through it while knowing the way back up. The obvious answer is having a 'parent' property.
I have the following json tree:
{
"Guid": "08967257-9306-4717-a76a-e1a4f0050505",
"Parent": null,
"Title": "Main Menu",
"Message": "A sample message",
"Elements": [
{
"$type": "Menu",
"Guid": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
"Parent": "08967257-9306-4717-a76a-e1a4f0050505",
"Title": "Option 1",
"Message": "Another sample message",
"Elements": [
{
"$type": "MenuEntry",
"Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
"Title": "Entry 1",
"Message": "Another sample message"
},
{
"$type": "MenuEntry",
"Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
"Title": "Entry 2",
"Message": "Another sample message"
},
{
"$type": "MenuEntry",
"Parent": "26dfca59-9163-4b11-8033-e8ad13f3f5cc",
"Title": "Entry 3",
"Message": "Another sample message"
}
]
},
{
"$type": "MenuEntry",
"Parent": "08967257-9306-4717-a76a-e1a4f0050505",
"Title": "Option 2",
"Message": "Another sample message"
}
]
}
This isn't about the deserialization itself, since that's working as it should.
My problem is that I'll have to access the parents after deserializing the file to a 'Menu' (the root).
I can think of two ways:
Find a 'parent' in runtime using its Guid - can be bad performance-wise depending on tree size;
Add a 'Menu' property to both 'Menu' and 'MenuEntry' classes to store the 'parent' and fill it, by finding the corresponding Guid, after deserializing from json. Basically, I'd put it together as soon as the application starts, preventing the find in runtime. It could be bad performance-wise as well.
What's the way to go? And how should I do it?
As a side note, I'm using Newtonsoft.Json
Thanks for the help!
#Ian Mercer comment actually helped me figure out a simple solution (using the 2nd way described in my post above).
The 2nd way doesn't really rely on Guids. After the deserialize, I can just iterate over each menu collection (as I probably would anyway) and give the reference to the 'parent' directly.
private void ConnectTree(Menu menu)
{
foreach (IMenuComponent component in menu.Elements) {
if (component is Menu) {
(component as Menu).ParentMenu = menu;
ConnectTree (component as Menu);
}
else if (component is MenuEntry) {
(component as MenuEntry).ParentMenu = menu;
}
}
}
Just need to call ConnectTree(_deserializedMenu)
Edit:
#Brian Rogers just mentioned PreserveReferencesHandling property from json net. The original problem was all about json not supporting circular references, but that solves it - it's the same logic as using Guids. There are multiple solutions, which is always good to know.
Thanks!

function in JSON format - possible?

I have the below JSON (been snipped for space), as you can see in the "test" and "tooltip" I have a property that needs to contain a function "formatter" (note this JSON is read in from an XML file and converted to JSON in .NET)
{
"test": {
"formatter": function(){return '<b>'+ this.point.name +'<\/b>: '+ this.y +' %';}
},
"title": {
"align": "center",
"text": "Your chart title here"
},
"tooltip": {
"formatter": function(){return '<b>'+ this.point.name +'<\/b>: '+ this.y +' %';}
}
}
Unfortunatly I'm getting an error on the ASPX page that produces the JSON file
There was an error parsing the JSON document. The document may not be well-formed.
This error is due to the fact that the bit after the "formatter" is not in quotation marks as it thinks it's a string. but If I put a string around it then the front end html page that uses the JSON doesn't see the function.
Is it possible to pass this as a function and not a string?
Many thanks.
Edit:
Thanks for the quick replys. As I said I know that the above isn't correct JSON due to the fact that the "function(){...}" part isn't in quote marks. The front end that reads the JSON file is 3rd party so I was wondering how I could pass the function through, I understand about the problems of injection (from a SQL point of view) and understand why it's not possible in JSON (not worked with JSON before).
If you passed it as a string you could use Javascripts EVAL function, but EVAL is EVIL.
What about meeting it half way and using Object Notation format ?
This is a template jquery plugin that I use at work, the $.fn.extend shows this notation format.
/*jslint browser: true */
/*global window: true, jQuery: true, $: true */
(function($) {
var MyPlugin = function(elem, options) {
// This lets us pass multiple optional parameters to your plugin
var defaults = {
'text' : '<b>Hello, World!</b>',
'anotherOption' : 'Test Plugin'
};
// This merges the passed options with the defaults
// so we always have a value
this.options = $.extend(defaults, options);
this.element = elem;
};
// Use function prototypes, it's a lot faster.
// Loads of sources as to why on the 'tinternet
MyPlugin.prototype.Setup = function()
{
// run Init code
$(this.element).html(this.options.text);
};
// This actually registers a plugin into jQuery
$.fn.extend({
// by adding a jquery.testPlugin function that takes a
// variable list of options
testPlugin: function(options) {
// and this handles that you may be running
// this over multiple elements
return this.each(function() {
var o = options;
// You can use element.Data to cache
// your plugin activation stopping
// running it again;
// this is probably the easiest way to
// check that your calls won't walk all
// over the dom.
var element = $(this);
if (element.data('someIdentifier'))
{
return;
}
// Initialise our plugin
var obj = new MyPlugin(element, o);
// Cache it to the DOM object
element.data('someIdentifier', obj);
// Call our Setup function as mentioned above.
obj.Setup();
});
}
});
})(jQuery);

Categories

Resources