C# Unity Reading data from JSON into an Object Array - c#

I am trying to read from a JSON file a series of items into an Object array.
Unfortunately it comes back as null.
Very similar to this issue Unity C# JsonUtility is not serializing a list
So in my particular situation I have the class for the item:
[Serializable]
public class StateData
{
public string name;
public string location;
public string gameText;
}
The class of the collection of items:
[Serializable]
public class StateDataCollection
{
public List<StateData> stateDatas = new List<StateData>();
}
And the class where this is invoked:
public class JSONReader : MonoBehaviour
{
private const string Path = #"S:\repos\myProject\Assets\gameText\";
void Start()
{
string json = File.ReadAllText(Path + "StateData.json");
StateDataCollection stateDataCollection = new StateDataCollection();
stateDataCollection = JsonUtility.FromJson<StateDataCollection>(json);
}
}
Finally the json
{
"StateData":[
{
"name": "name",
"location":"location",
"gameText" : "gameText"},
{
"name": "name",
"location":"location",
"gameText" : "gameText"},
{
"name": "name",
"location":"location",
"gameText" : "gameText"
}]
}
The object comes back as null.
The file is reading OK.
Any ideas where this is failing?
TY!

I would start by checking a few things first:
Check if the file actually exists. Just use a small Debug.Log(File.Exists(path)).
Try another path -> something save like: Application.persistentDataPath. On Windows this will give you something like: %userprofile%\AppData\Local\Packages<productname>\LocalState. This ensures you being able to read/write from/to files.
Fill the List with actual data so you can differentiate between a saved list and the default list.
Edit 1:
I just replicated your Code and made some adjustments to it. It's not perfect but it definately worked for me.
StateData:
using System;
[Serializable]
public struct StateData
{
public string m_Name;
public string m_Location;
public string m_GameText;
public StateData(string name, string location, string gameText)
{
m_Name = name;
m_Location = location;
m_GameText = gameText;
}
}
StateDataCollection:
using System;
using System.Collections.Generic;
[Serializable]
public class StateDataCollection
{
public List<StateData> m_StateData;
public StateDataCollection() => m_StateData = new List<StateData>();
}
JSONReader:
using System.IO;
using UnityEngine;
public class JSONReader : MonoBehaviour
{
private const string FILENAME = "StateData.json";
private string m_path;
public string FilePath {
get => m_path;
set => m_path = value;
}
void Start()
{
FilePath = Path.Combine(Application.persistentDataPath, FILENAME);
Debug.Log(FilePath);// delete me
StateDataCollection stateDataCollection = new StateDataCollection();
stateDataCollection.m_StateData.Add(new StateData("Peter", "X", "123"));
stateDataCollection.m_StateData.Add(new StateData("Annie", "Y", "42"));
stateDataCollection.m_StateData.Add(new StateData("TheGuyFromTheSupermarket", "Supermarket", "666"));
SaveStateDataCollectionToPath(FilePath, stateDataCollection);
LoadStateDataCollectionFromPath(FilePath);
}
private void SaveStateDataCollectionToPath(string path, StateDataCollection stateDataCollection)
{
// ToDo: some checks on path and stateDataCollection
string json = JsonUtility.ToJson(stateDataCollection, true);// prettyPrint active
File.WriteAllText(path, json);
}
private void LoadStateDataCollectionFromPath(string path)
{
if(File.Exists(path)) {
Debug.Log($"File exists at: {path}");// delete me
string json = File.ReadAllText(path);
StateDataCollection stateDataCollection = JsonUtility.FromJson<StateDataCollection>(json);
// delete me
Debug.Log($"StateDataCollection count: { stateDataCollection.m_StateData.Count}");
int i = 1;
stateDataCollection.m_StateData.ForEach(o => {
Debug.Log(
$"Item Nr.: {i}\n" +
$"Name: {o.m_Name}\n" +
$"Location: {o.m_Location}\n" +
$"GameText: {o.m_GameText}\n\n");
++i;
});
}
}
}
I also added a few Debug.Log()s for testing.
Usage:
Run it once with SaveStateDataCollectionToPath(FilePath, stateDataCollection); active and once without it. The Console should display 3 items.
Edit 2:
I think your issue might have been naming-convention violation. File requires using System.IO;, which beside File also contains Path. Unfortunately you also named your const string "Path".

Related

Setting an Integer to something, but It won't change. Why?

Currently I have this function that loads an integer from a JSON file, and then it sets the ExeBalance to the Integer it found in the JSON file however when checking the breakpoints I see that the JSON JBalance gets retrieved correctly, but it won't change the ExeBalance integer.
It is setting it to the JSON object, but It won't change the ExeBalance value:
ExeBalance = saveDataJson.JBalance;
This is my code:
namespace Money_Simulator
{
public class DataObject
{
public int JBalance { get; set; }
}
internal class DataHandler
{
public int ExeBalance = 0;
public void AddBalance(int amt)
{
ExeBalance = ExeBalance + 1;
Console.WriteLine(ExeBalance);
}
public void LoadSave()
{
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"savedata.json"
);
StreamReader sr = new StreamReader(filePath);
string saveDataContent = sr.ReadToEnd();
var saveDataJson = JsonConvert.DeserializeObject<DataObject>(
saveDataContent
);
ExeBalance = saveDataJson.JBalance;
Console.WriteLine("ExeBalance was set to this value from reading savedata.json:");
Console.WriteLine(ExeBalance);
}
}
}
The contents of savedata.json are {"JBalance": 5}.
The problem with your code is that you don't understand how this should work. My guess is you would like to add an amount to the balance that is stored in your JSON. I've modified your code to do just that. The Load() returns the deserialized JSON object. The AddBalance then adds the amount (amt) and the Save then saves it to the JSON file. Here's the modified code:
using Newtonsoft.Json;
namespace Money_Simulator
{
public class DataObject
{
public int JBalance { get; set; }
}
internal class DataHandler
{
private const string FileName = #"..\..\..\savedata.json";
public void AddBalance(int amt)
{
var data = Load();
data.JBalance = data.JBalance + amt;
Save(data);
}
private DataObject Load()
{
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, FileName);
string saveDataContent = File.ReadAllText(filePath);
var saveDataJson = JsonConvert.DeserializeObject<DataObject>(saveDataContent);
Console.WriteLine($"ExeBalance was set to this value from reading savedata.json: {saveDataJson.JBalance}");
return saveDataJson;
}
private void Save(DataObject data)
{
var output = JsonConvert.SerializeObject(data);
File.WriteAllText(FileName, output);
}
}
}
In your main application do the following:
using Money_Simulator;
var handler = new DataHandler();
handler.AddBalance(1);
handler.AddBalance(1);
handler.AddBalance(1);
handler.AddBalance(1);
handler.AddBalance(1);
handler.AddBalance(1);
handler.AddBalance(1);
Result is:
ExeBalance was set to this value from reading savedata.json: 10
ExeBalance was set to this value from reading savedata.json: 11
ExeBalance was set to this value from reading savedata.json: 12
ExeBalance was set to this value from reading savedata.json: 13
ExeBalance was set to this value from reading savedata.json: 14
ExeBalance was set to this value from reading savedata.json: 15
ExeBalance was set to this value from reading savedata.json: 16

How to include values when getting an instance with reflection?

I am automating how to serialize a class with [Serializable] attribute to Json in Unity.
I tried to get the FieldInfo by reflection and pass that value to parse it as Json, but it failed. Empty curly braces are output.
Problem Code
public static class SerializeHelper
{
public static void SerializeObjectField()
{
var fieldsInfo = GameInfo.Instance.GetType().GetFields(
BindingFlags.Public | BindingFlags.Instance);
foreach (var fieldInfo in fieldsInfo)
{
ToJson(fieldInfo);
}
}
private static void ToJson(FieldInfo fieldInfo)
{
var json = JsonUtility.ToJson(fieldInfo, true);
Debug.Log(json); // Empty curly braces are output
}
}
public class GameInfo : MonoBehaviour
{
// Sigleton Pattern
public Gold gold = new();
public int Gold
{
get => gold.Amount;
set => gold.Amount = value;
}
// ...
}
[Serializable]
public class Gold
{
[SerializeField] private int amount;
public int Amount
{
get => amount;
set => amount = value;
}
}
If I write it manually without using reflection, it outputs just fine.
Correct Code
// ...
var json = JsonUtility.ToJson(GameInfo.Instance.gold, true);
Debug.Log(json);
// ...
Output
{
"amount": 43 // Create a button that increments by 1 for every click
}
Can you please tell me what I am doing wrong when using reflection?
You are missing one call, property info gives you a description of a field/property, which is related to class definition but has no reference to your actual instance.
To actually get the value you need to call GetValue method on the property info
try replacing
var json = JsonUtility.ToJson(fieldInfo, true);
with something similar to:
var json = JsonUtility.ToJson(fieldInfo.GetValue(instanceReference), true);
A working example below:
public class ReflectionTest : MonoBehaviour
{
[System.Serializable]
public class TestClass
{
public string myString = "test";
}
public TestClass testClassInstance = new TestClass();
void OnValidate()
{
var fieldInfo = this.GetType().GetField("testClassInstance");
var value = fieldInfo.GetValue(this);
var valueString = JsonUtility.ToJson(value);
Debug.Log($"after serialization {valueString}");
}
}

c# With Unity and LitJSON Getting an Invalid Cast Exception when trying to de-serialize back from JSONData object

I have been working on a save game solution for my project and have hit a wall. After several days of research, trial/ error etc. I decided to give the stack a shot. I am attempting to convert back from a Json text file into an object, then add it to a dictionary. It keeps giving my Invalid cast exceptions, regardless as to how I iterate through the Jdata object. Keeping in mind I am using LitJson 3rd party. Here is the code thus far. The error occurs at the foreach statement.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;
using System.IO;
public static class SaveGame {
public static string savePath = Application.persistentDataPath +
"/Saves/";
public static int numberOfSaves = 0;
public static string saveFileName = PlayerCrew.playerShipName + ".json";
public static void SavePlayerData ()
{
string playerSavePath = savePath + saveFileName;
string jsonHolder;
jsonHolder = JsonMapper.ToJson(PlayerCrew.playerCrewManifest);
if (!File.Exists(playerSavePath))
{
FileStream fs = new FileStream(playerSavePath,
FileMode.OpenOrCreate);
fs.Close();
File.WriteAllText(playerSavePath, jsonHolder);
}
else
{
File.WriteAllText(playerSavePath, jsonHolder);
}
}
public static void LoadCrewManifest()
{
string playerSavePath = savePath + saveFileName;
string jsonHolder;
jsonHolder = File.ReadAllText(playerSavePath);
JsonData jdata = JsonMapper.ToObject(jsonHolder);
PlayerCrew.playerCrewManifest.Clear();
foreach (KeyValuePair<string,CrewMember> item in jdata)
{
PlayerCrew.playerCrewManifest.Add(item.Key, item.Value);
Debug.Log(item.Key);
}
}
}
I recommend you to use NetJson. You can Deserialize using a generic type, including Dictionary.
The values in jdata might come as KeyValuePair<string, string>
the simplest way would be a simple constructor for your class CrewMember e.g. something like
[Serializable]
public class CrewMember
{
public string Name;
public CrewMember(string name)
{
Name = name;
}
}
Than you don't want the item.key since it will be the variable name (in this case Name) instead you want the item.Value.
Your json code could look like
JsonData jdata = JsonMapper.ToObject(jsonHolder);
PlayerCrew.playerCrewManifest.Clear();
foreach (KeyValuePair<string,string> item in jdata)
{
PlayerCrew.playerCrewManifest.Add(item.Value, new CrewMember(item.Value));
Debug.Log(item.Value);
}

Convert a string to JSON object

I have a JsonString:
"baseAbilities":"[{"baseId":1,"name":"Focused Elemental Strike"}]"
I want to make it:
"baseAbilities":[{"baseId":1,"name":"Focused Elemental Strike"}]
So the it is easy for JSON utility to deserialise it. Any suggestions
EDIT:
This is basically a json object in the following Json String:
{"data”: [{"id":9,"name":"Sam5","baseAbilities":"[{\"baseId\":1,\"name\":\"Focused Elemental Strike\"},{\"baseId\":9,\"name\":\"Cleanse\"}]"]}
The backend developer saved JSON in a column baseAbilities as a string. Now I don't have the access to Back end APIs. So i need to convert this string baseAbilities to JSON so that I can access objects inside it.
Now I use these classes to decode JSON:
[System.Serializable]
class GetBAResult
{
public List<bounceAbilityData> data;
}
[System.Serializable]
class bounceAbilityData
{
public int id;
public string name;
public string baseAbilities;
}
And this is how I decode JSON:
GetBAResult P = JsonUtility.FromJson<GetBAResult>(w.text);
for (int i = 0; i < P.data.Count; i++)
{
Debug.Log(P.data[i].name);
GameObject layoutGroupNameButton = (GameObject)Instantiate(prefabBAButton);
layoutGroupNameButton.transform.SetParent(ParentPanel, false);
layoutGroupNameButton.GetComponentInChildren<Text>().text = P.data[i].name;
layoutGroupNameButton.GetComponent<BounceAbilityButton>().id = P.data[i].id;
layoutGroupNameButton.GetComponent<BounceAbilityButton>().name = P.data[i].name;
Debug.Log(P.data[i].baseAbilities);
}
I need to get things inside baseAbilities such as "baseID" and "name"
Have you tried this?
JObject json = JObject.Parse(str);
documentation
With a class set of
[System.Serializable]
class GetBAResult
{
public List<bounceAbilityData> data;
}
[System.Serializable]
class bounceAbilityData
{
public int id;
public string name;
public List<BaseAbilities> baseAbilities;
}
[System.Serializable]
class BaseAbilities
{
public int baseId;
public string name;
}
You can then use the following to Deserialize your JSON to an instance of GetBAResult
string json = "{\"data\":[{\"id\":9,\"name\":\"Sam5\",\"baseAbilities\":[{\"baseId\":1,\"name\":\"Focused Elemental Strike\"},{\"baseId\":9,\"name\":\"Cleanse\"}]}]}";
GetBAResult myObject = JsonUtility.FromJson<GetBAResult>(json);
BaseAbilities abilities = myObject.data[0].baseAbilities[0];
You can read more about JSON Deserialization here on the Unity Documentation
https://docs.unity3d.com/Manual/JSONSerialization.html
The original questions says that you want to change:
"baseAbilities":"[{"baseId":1,"name":"Focused Elemental Strike"}]"
to
"baseAbilities":[{"baseId":1,"name":"Focused Elemental Strike"}]
Basically, you want to remove the " before the [{" then remove the final " at the end of the json string. The problem is that even after you do this, "baseAbilities":[{"baseId":1,"name":"Focused Elemental Strike"}] cannot be serialized to json.
You need to add { in front of it and } at the end of it.
The final string after adding { in front of it and } at the end is:
{"baseAbilities":[{"baseId":1,"name":"Focused Elemental Strike"}]}
The fixJson function below fixes those problems and this is how to use it:
[System.Serializable]
public class BaseAbility
{
public int id;
public string name;
}
[System.Serializable]
public class GetBAResult
{
public List<BaseAbility> baseAbilities;
}
void Start()
{
// StartCoroutine(RespawnPlayer());
string fixedJson = fixJson(w.text);
//string fixedJson = fixJson("\"baseAbilities\":\"[{\"id\":1,\"name\":\"Focused Elemental Strike\"}]\"");
Debug.Log("Fixed Json: " + fixedJson);
GetBAResult P = JsonUtility.FromJson<GetBAResult>(fixedJson);
Debug.Log("Count Json: " + P.baseAbilities.Count);
for (int i = 0; i < P.baseAbilities.Count; i++)
{
Debug.Log("Name: " + P.baseAbilities[i].name);
Debug.Log("Base ID: " + P.baseAbilities[i].id);
}
}
string fixJson(string jsonToFix)
{
//First srting with `"` that will be removed
const string firstString = "\"baseAbilities\":\"";
//Last string with `"` that will be removed
const string lastString = "\"";
//String that will be used to fix the "baseAbilities":"[ with the `"`
const string fixedFirstString = "\"baseAbilities\":";
//Get the first Index of firstString
int firstIndex = jsonToFix.IndexOf(firstString);
//Remove First Index of `firstString`
jsonToFix = jsonToFix.Remove(firstIndex, firstString.Length);
int lastIndex = jsonToFix.LastIndexOf(lastString);
//Remove everything from Last `lastString` to the end
jsonToFix = jsonToFix.Remove(lastIndex);
//Append the correct/fixed string without `"` to the beginning of the json data
jsonToFix = fixedFirstString + jsonToFix;
//Add `{` to the begging and `}` to the end of the json data
jsonToFix = "{" + jsonToFix + "}";
return jsonToFix;
}
Let's say you have a class like this:
using System;
using System.Collections.Generic;
using UnityEngine;
public class JsonTest : MonoBehaviour
{
[Serializable]
public class Ability
{
public int baseId;
public string name;
public Ability(int _baseId, string _name)
{
baseId = _baseId;
name = _name;
}
}
public class Abilities
{
public List<Ability> baseAbilities;
public Abilities(List<Ability> _baseAbilities)
{
baseAbilities = _baseAbilities;
}
}
void Start()
{
List<Ability> list = new List<Ability>();
list.Add(new Ability(1, "Focused Elemental Strike"));
list.Add(new Ability(2, "Another ability"));
Abilities baseAbilities = new Abilities(list);
string serializedAbilites = JsonUtility.ToJson(baseAbilities);
Debug.Log(serializedAbilites);
Abilities deserializedAbilites = JsonUtility.FromJson<Abilities>(serializedAbilites);
}
}
You have an Ability class marked as Serializable containing and id and a name.
You have another class Abilities used as a wrapper for a list of abilities.
You can then serialize a list of abilities using JsonUtility.ToJson(). Which will give you the following string:
{
"baseAbilities": [
{
"baseId": 1,
"name": "Focused Elemental Strike"
},
{
"baseId": 2,
"name": "Another ability"
}
]
}
And of course you can deserialize it using JsonUtility.FromJson().
Is this what your looking for ?

C# custom object in combobox

I am relatively new to C# (WinForms), and had a question regarding combo boxes. I have a combo box of Reviewer objects (it is a custom class with an overridden ToString method) and am currently attempting to go through all the checked items and use them to generate a setup file.
Here is how the combo box is populated (populated on form load). Parameters is just a collection of linked lists and parsing code.
for (int i = 0; i < parameters.GetUsers().Count; i++)
{
UserList.Items.Add(parameters.GetUsersArray()[i], parameters.GetUsersArray()[i].isSelected());
}
Here is how I am trying to read it. setup is a StringBuilder. The problem is that GetID is not defined. Does the add function above cast the Reviewer object to a Object object? It looks a little funny since it creates a file fed into a Perl script. A sample desired output line looks like this: inspector0 => "chg0306",
for (int i = 0; i < UserList.CheckedItems.Count; i++)
{
setup.AppendLine("inspector" + i.ToString() + " => \t \"" +
UserList.CheckedItems[i].GetID() + "\",");
}
Here is the users class: (Sample User is ID = aaa0000 name: Bob Joe)
public class Reviewer
{
private string name;
private string id;
private bool selected;
public Reviewer(string newName, string newID, bool newSelected)
{
name = newName;
id = newID;
selected = newSelected;
}
public string GetName()
{
return name;
}
public override string ToString()
{
//string retVal = new string(' ', id.Length + name.Length + 1);
string retVal = id + '\t' + name;
return retVal;
}
public string GetID()
{
return id;
}
public bool isSelected()
{
return selected;
}
}
For posterity, here is the Parameters class:
public class ParameterLists
{
public ParameterLists()
{
projects = new LinkedList<string>();
reviewers = new LinkedList<Reviewer>();
}
public enum FileContents {
PROJECT_LIST,
USERS_LIST,
}
public LinkedList<Reviewer> GetUsers()
{
return reviewers;
}
public LinkedList<string> GetProjects()
{
return projects;
}
public Reviewer[] GetUsersArray()
{
Reviewer[] userArray = new Reviewer[reviewers.Count];
reviewers.CopyTo(userArray, 0);
return userArray;
}
public string[] GetProjectsArray()
{
String[] projectArray = new String[projects.Count];
projects.CopyTo(projectArray, 0);
return projectArray;
}
public void LoadParameters(string fileName)
{
//Reads the parameters from the input file.
}
private void CreateDefaultFile(string fileName)
{
// Create the file from the defaultfile , if it exists.
// Otherwise create a blank default file.
}
private LinkedList <string> projects;
private LinkedList <Reviewer> reviewers;
}
I am probably missing something simple, coming from embedded C++. Any help would be appreciated.
You have to cast that object:
((Reviewer)UserList.CheckedItems[i]).GetID()

Categories

Resources