I'm developing a software to manage my collection of coins. I need to export the content of a list of objects in a JSON file but I encounter this error everytime I want to display the coins that are actually inside the database:
Additional text encountered after finished reading JSON content: [. Path '', line 1, position 109.
Here's where everything should happen:
List<Coin> coins = new List<Coin>();
public bool AddACoin (int ID, String coinName, String coinNation, String coinStatus, int coinYear, int quantity, float value)
{
var jsonSerializer = new JsonSerializer();
using (StreamWriter streamWriter = new StreamWriter(path, true))
using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
{
coins.Add(new Coin(ID, coinName, coinNation, coinStatus, coinYear, quantity, value));
jsonSerializer.Serialize(jsonWriter, coins.ToList());
}
return true;
}
The output is stored inside different blocks of square brackets. I've a block for every object inserted. Instead I should have every object inside a unique block of square brackets. Thanks in advance.
EDIT: Here's the content of the JSON file
[{"ID":0,"coinName":"1 Euro","coinNation":"Ita","coinStatus":"FdC","coinYear":2005,"quantity":1,"value":4.7}][{"ID":0,"coinName":"1 Euro","coinNation":"Ita","coinStatus":"FdC","coinYear":2005,"quantity":1,"value":4.7},{"ID":1,"coinName":"2 Euro","coinNation":"Bel","coinStatus":"FdC","coinYear":2004,"quantity":1,"value":30.0}]
As I said, everything should be inside a unique block of square brackets.
I think that I've just found the solution to my problem and I'm going to share it with you. I've changed some lines and now I have:
public bool AddACoin (int ID, String coinName, String coinNation, String coinStatus, int coinYear, int quantity, float value)
{
var jsonSerializer = new JsonSerializer();
using (StreamReader streamReader = new StreamReader(path, true))
{
string json = streamReader.ReadToEnd();
coins = JsonConvert.DeserializeObject<List<Coin>>(json);
coins.Add(new Coin(ID, coinName, coinNation, coinStatus, coinYear, quantity, value));
string newJson = JsonConvert.SerializeObject(coins);
streamReader.Close();
File.WriteAllText(path, newJson);
}
return true;
}
If I'm thinking correctly, doing this causes the program to read until it reaches EOF and then, after serializing/deserializing the list, appends the new object. At the moment this seems to works fine.
I recommend you to use NewtonsoftJSON (you can install it via NuGet), clear json file every time you adding new coin, there are coins manager sample for you:
public class CoinsManager
{
public List<Coin> Coins { get; set; }
public string FilePath { get; set; }
public CoinsManager(string filePath)
{
FilePath = filePath;
Coins = new List<Coin>();
}
public void LoadCoins()
{
if (File.Exists(FilePath))
{
//If file exists, but empty, save empty settings to it
if (new FileInfo(FilePath).Length == 0)
{
SaveSettings();
}
else
{
//Read json from file
using (StreamReader r = new StreamReader(FilePath))
{
string json = r.ReadToEnd();
//Convert json to list
Coins = JsonConvert.DeserializeObject<List<Coin>>(json);
}
}
}
else
{
//Create file
File.Create(FilePath).Close();
//Wait for filesystem to create file
while (!File.Exists(FilePath))
{
System.Threading.Thread.Sleep(100);
}
//Save empty settings to file
SaveSettings();
}
}
public void SaveSettings()
{
string json = JsonConvert.SerializeObject(Coins);
File.WriteAllText(FilePath, json);
}
//Can save or update passed coin
public void SaveCoin(Coin coin)
{
//Select old coin
var oldCoin = Coins.Where(c => c.ID == coin.ID).FirstOrDefault();
//If there was no old coin, get last existing coin id, or zero if Coins list is empty
if (oldCoin == null)
{
int lastId;
if (Coins.Count != 0)
lastId = Coins.Count - 1;
else
lastId = 0;
coin.ID = lastId + 1;
Coins.Add(coin);
}
else
{
int index = Coins.IndexOf(oldCoin);
Coins[index] = coin;
}
}
public void DeleteCoin(Coin coin)
{
Coins.RemoveAll(c => c.ID == coin.Id);
}
}
and it's usage:
CoinsManager coinsManager = new CoinsManager("coinsStorage.json");
coinsManager.LoadCoins();
coinsManager.SaveCoin(new Coin {
...
});
coinsManager.SaveSettings();
if i understand correct you just need to change this row:
StreamWriter streamWriter = new StreamWriter(path, true);
to this one:
StreamWriter streamWriter = new StreamWriter(path, false);
your problem is that you always add to the file new json with all the list instead of just writing the list.
Because you work with file you need and you want to append your only option is to read the file then add elements and write it again.
You can read it when application start and menage it like it seems you do becouse your list is global.
Or you can read it right before you want to write the file.
In any one of this cases you need to add the fix I wrote.
You can use this for read the json o your list:
string myJsonString = File.ReadAllText(path);
coins = JsonConvert.DeserializeObject<List<Coin>>(myJsonString);
here is full function:
public bool AddACoin (int ID, String coinName, String coinNation, String coinStatus, int coinYear, int quantity, float value)
{
var jsonSerializer = new JsonSerializer();
using (StreamWriter streamWriter = new StreamWriter(path, false))
using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
{
string myJsonString = File.ReadAllText(path);
coins = JsonConvert.DeserializeObject<List<Coin>>(myJsonString);
coins.Add(new Coin(ID, coinName, coinNation, coinStatus, coinYear, quantity, value));
jsonSerializer.Serialize(jsonWriter, coins.ToList());
}
return true;
}
Related
i have a lass like this
public class Params
{
public string FirstName;
public string SecondName;
public string Path;
public long Count;
public double TotalSize;
public long Time;
public bool HasError;
public Params()
{
}
public Params(string firstName, string secondName, string path, long count, double totalSize, long time, bool hasError)
{
FirstName = firstName;
SecondName = secondName;
Path = path;
Count = count;
TotalSize = totalSize;
Time = time;
HasError = hasError;
}
}
I have the json class like this:
public static class FileWriterJson
{
public static void WriteToJsonFile<T>(string filePath, T objectToWrite, bool append = true) where T : new()
{
TextWriter writer = null;
try
{
var contentsToWriteToFile = JsonConvert.SerializeObject(objectToWrite);
writer = new StreamWriter(filePath, append);
writer.Write(contentsToWriteToFile);
}
finally
{
if (writer != null)
writer.Close();
}
}
public static T ReadFromJsonFile<T>(string filePath) where T : new()
{
TextReader reader = null;
try
{
reader = new StreamReader(filePath);
var fileContents = reader.ReadToEnd();
return JsonConvert.DeserializeObject<T>(fileContents);
}
finally
{
if (reader != null)
reader.Close();
}
}
}
The main program is like this
var Params1 = new Params("Test", "TestSecondName", "Mypath",7, 65.0, 0, false);
FileWriterJson.WriteToJsonFile<Params>("C:\\Users\\myuser\\bin\\Debug\\test1.json", Params1);
FileWriterJson.WriteToJsonFile<Params>("C:\\Users\\myuser\\bin\\Debug\\test1.json", Params1);
This is mine test1.json:
{"FirstName":"Test","SecondName":"TestSecondName","Path":"Mypath","Count":7,"TotalSize":65.0,"Time":0,"HasError":false}{"FirstName":"Test","SecondName":"TestSecondName","Path":"Mypath","Count":7,"TotalSize":65.0,"Time":0,"HasError":false}
As you can see i have two json objects written in the file.
What i need to do is:
void ReadAllObjects(){
//read the json object from the file
// count the json objects - suppose there are two objects
for (int i=0;i<2;i++){
//do some processing with the first object
// if processing is successfull delete the object (i don't know how to delete the particular json object from file)
} }
but when i read like this
var abc =FileWriterJson.ReadFromJsonFile<Params>(
"C:\\Users\\myuser\\bin\\Debug\\test1.json");
i get the following error:
"Additional text encountered after finished reading JSON content: {.
Path '', line 1, position 155."
Then i used the following code to read the JSON file
public static IEnumerable<T> FromDelimitedJson<T>(TextReader reader, JsonSerializerSettings settings = null)
{
using (var jsonReader = new JsonTextReader(reader) { CloseInput = false, SupportMultipleContent = true })
{
var serializer = JsonSerializer.CreateDefault(settings);
while (jsonReader.Read())
{
if (jsonReader.TokenType == JsonToken.Comment)
continue;
yield return serializer.Deserialize<T>(jsonReader);
}
}
}
}
Which worked fine for me.
Now i need following suggestion:
1> when i put my test1.json data in https://jsonlint.com/ it says Error:
Parse error on line 9:
..."HasError": false} { "FirstName": "Tes
----------------------^
Expecting 'EOF', '}', ',', ']', got '{'
should i write into file in some other way.
2>Is there any better of doing this.
You are writing each object out individually to the file.
But what you are creating is not a valid JSON file, just a text file with individual JSON objects.
To make it valid JSON, then you need to put the objects into an array or list and then save this to the file.
var Params1 = new Params("Test", "TestFirstName", "Mypath",7, 65.0, 0, false);
var Params2 = new Params("Test 2", "TestSecondName", "Mypath",17, 165.0, 10, false);
List<Params> paramsList = new List<Params>();
paramsList .Add(Params1);
paramsList .Add(Params2);
FileWriterJson.WriteToJsonFile<List<Params>>("C:\\Users\\myuser\\bin\\Debug\\test1.json", paramsList);
FileWriterJson.WriteToJsonFile("C:\Users\myuser\bin\Debug\test1.json", Params1);
Then you should be able to read it in OK. Don't forget to read in a List<Params>
Using C#. I am creating a program that stores movies in a text file such as:
38#Finding Nemo#Family#2001#yes
36#Wonder Woman#Action#2017#yes
35#Solo#Action#2018#yes
I am trying to process a batch transaction into my movie inventory file. This text file holds an action code after the first delimiter('#') whether to add, change, or delete a movie in the movie inventory file. The problem that I am having is that each line has a different format while I am trying to store it into an array. What is a way to read the header line differently and ignore empty values between delimiters?
An example batch file is:
H#Movie Inventory Updates#10/31/2019
D#C#5#Family Movie 1###no
D#A#21#Sci-Fi Movie 1#sci-fi#2005#yes
D#A#22#Other Movie 1#other#2001#yes
D#C#1###2002#
D#D#4####
public static Batch[] ReadBatchFile()
{
Batch[] bTransactions = new Batch[100];
if (File.Exists("batch_transaction.txt"))
{
StreamReader inFile = new StreamReader("batch_transaction.txt");
//string path = "batch_transaction.txt";
string line = inFile.ReadLine();
while (line != null) //read all transaction records, ignoring header and footer
{
string[] tempArray = line.Split('#');
bTransactions[Batch.GetCount()] = new Batch(tempArray[0], tempArray[1], int.Parse(tempArray[2]), tempArray[3], tempArray[4], double.Parse(tempArray[5]), tempArray[6]);
Batch.SetCount(Batch.GetCount() + 1);
line = inFile.ReadLine();
}
inFile.Close();
}
else
{
Console.WriteLine("File not found.");
}
return bTransactions;
}
}
Here is how I'm currently trying to read in the the other lines:
public Batch(string recordType, string actionCode, int movieId, string movieTitle, string movieGenre, double releaseYear, string inStock)
{
this.recordType = recordType;
this.actionCode = actionCode;
this.movieId = movieId;
this.movieTitle = movieTitle;
this.movieGenre = movieGenre;
this.releaseYear = releaseYear;
}
I have a csv file with the following data:
500000,0.005,6000
690000,0.003,5200
I need to add each line as a separate array. So 50000, 0.005, 6000 would be array1. How would I do this?
Currently my code adds each column into one element.
For example data[0] is showing 500000
690000
static void ReadFromFile(string filePath)
{
try
{
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filePath))
{
string line;
// Read and display lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null)
{
string[] data = line.Split(',');
Console.WriteLine(data[0] + " " + data[1]);
}
}
}
catch (Exception e)
{
// Let the user know what went wrong.
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
Using the limited data set you've provided...
const string test = #"500000,0.005,6000
690000,0.003,5200";
var result = test.Split('\n')
.Select(x=> x.Split(',')
.Select(y => Convert.ToDecimal(y))
.ToArray()
)
.ToArray();
foreach (var element in result)
{
Console.WriteLine($"{element[0]}, {element[1]}, {element[2]}");
}
Can it be done without LINQ? Yes, but it's messy...
const string test = #"500000,0.005,6000
690000,0.003,5200";
List<decimal[]> resultList = new List<decimal[]>();
string[] lines = test.Split('\n');
foreach (var line in lines)
{
List<decimal> decimalValueList = new List<decimal>();
string[] splitValuesByComma = line.Split(',');
foreach (string value in splitValuesByComma)
{
decimal convertedValue = Convert.ToDecimal(value);
decimalValueList.Add(convertedValue);
}
decimal[] decimalValueArray = decimalValueList.ToArray();
resultList.Add(decimalValueArray);
}
decimal[][] resultArray = resultList.ToArray();
That will give the exact same output as what I've done with the first example
If you may use a List<string[]> you do not have to worry about the array length.
In the following example, the variable lines will be a list arrays, like:
["500000", "0.005", "6000"]
["690000", "0.003", "5200"]
static void ReadFromFile(string filePath)
{
try
{
// Create an instance of StreamReader to read from a file.
// The using statement also closes the StreamReader.
using (StreamReader sr = new StreamReader(filePath))
{
List<string[]> lines = new List<string[]>();
string line;
// Read and display lines from the file until the end of
// the file is reached.
while ((line = sr.ReadLine()) != null)
{
string[] splittedLine = line.Split(',');
lines.Add(splittedLine);
}
}
}
catch (Exception e)
{
// Let the user know what went wrong.
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
While other have split method, I will have a more "scolar"-"specified" method.
You have some Csv value in a file. Find a name for this object stored in a Csv, name every column, type them.
Define the default value of those field. Define what happends for missing column, and malformed field. Header?
Now that you know what you have, define what you want. This time again: Object name -> Property -> Type.
Believe me or not, the simple definition of your input and output solved your issue.
Use CsvHelper to simplify your code.
CSV File Definition:
public class CsvItem_WithARealName
{
public int data1;
public decimal data2;
public int goodVariableNames;
}
public class CsvItemMapper : ClassMap<CsvItem_WithARealName>
{
public CsvItemMapper()
{ //mapping based on index. cause file has no header.
Map(m => m.data1).Index(0);
Map(m => m.data2).Index(1);
Map(m => m.goodVariableNames).Index(2);
}
}
A Csv reader method, point a document it will give your the Csv Item.
Here we have some configuration: no header and InvariantCulture for decimal convertion
private IEnumerable<CsvItem_WithARealName> GetCsvItems(string filePath)
{
using (var fileReader = File.OpenText(filePath))
using (var csvReader = new CsvHelper.CsvReader(fileReader))
{
csvReader.Configuration.CultureInfo = CultureInfo.InvariantCulture;
csvReader.Configuration.HasHeaderRecord = false;
csvReader.Configuration.RegisterClassMap<CsvItemMapper>();
while (csvReader.Read())
{
var record = csvReader.GetRecord<CsvItem_WithARealName>();
yield return record;
}
}
}
Usage :
var filename = "csvExemple.txt";
var items = GetCsvItems(filename);
I was able to create an XML serializer in Unity which saves my game state and was working fine. I haven't touched it since then, but a few days later it started saving the XML files on one line instead.
This was how it looked before:
<GameData xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<gameFlags/>
<memories/>
<lastSaveTime>-8586819040232916923</lastSaveTime>
<health>50</health>
<speed>0.27</speed>
<playerPosition>
<x>10</x>
<y>-48.2583</y>
<z>0</z>
</playerPosition>
<lastSaveFile/>
<savedScene>Area 0-0</savedScene>
<playerName>Jon</playerName>
<PlayerPositionX>10</PlayerPositionX>
<PlayerPositionY>-48.2583</PlayerPositionY>
<PlayerPositionZ>0</PlayerPositionZ>
</GameData>
This is how it looks now
-8586812865931894767 50 0.27 10 -48.2583 0 Default Area 0-0 Jon 10 -48.2583 0
Everything is all on one line. I reverted to my previous commit as well when it was working but it still is saving on one line. Does anyone have any experience with this change?
Save Game Method
public void SaveGame(string saveFile)
{
CheckDirectory();
gameData.generateGameData (GameObject.FindGameObjectWithTag("Player").GetComponent<Player>());
// Update saveFile name
if (saveFile == null)
{
saveFile = GenerateNewSaveName();
}
this.saveFile = saveFile;
// FileStream fs = File.Create(GameDic.Instance.SavePath + saveFile);
UpdateSaveData(saveFile);
string fullSavePath = SavePath + saveFile + FILE_EXTENSION;
FileStream fs;
// Create a file or open an old one up for writing to
if (!File.Exists(fullSavePath))
{
fs = File.Create(fullSavePath);
}
else
{
fs = File.OpenWrite(fullSavePath);
}
XmlSerializer serializer = new XmlSerializer(typeof(GameData));
TextWriter textWriter = new StreamWriter(fs, System.Text.Encoding.UTF8);
serializer.Serialize(textWriter, gameData);
fs.Close();
Debug.Log("Game Saved to " + fullSavePath);
}
GameData Class
[Serializable]
public class GameData
{
#region Public Fields
public List<GameFlag> gameFlags;
public List<string> memories;
public List<string> conversations;
public List<QuestInstance> questInstances;
public long lastSaveTime;
public float health, speed;
// Needs properties to access
[NonSerialized]
public Vector3 playerPosition;
public string lastSaveFile;
public string savedScene;
public string playerName;
#endregion Public Fields
#region Public Constructors
public void generateGameData(Player player) {
health = player.getStat (Stat.HP);
speed = player.getStat (Stat.SPEED);
savedScene = SceneManager.GetActiveScene ().name;
playerPosition = player.respawnPoint;
playerName = "Jon";
memories = player.GetComponentInChildren<MemoryManager> ().getAccessedMemories ();
conversations = player.GetComponentInChildren<NPCManager> ().getConversations ();
questInstances = player.GetComponent<QuestManager> ().getQuestInstances ();
}
public string GenerateNewSaveName()
{
int attempt = 0;
string newSaveName = "";
while (newSaveName == "")
{
// Save Name is Player Name
string checkString = gameData.playerName;
// Add a number if original already taken
if (attempt != 0) checkString += attempt;
if (!File.Exists(SavePath + checkString))
{
// Make the check string the new file name
newSaveName = checkString;
}
attempt++;
}
return newSaveName;
}
}
For the QuestInstance class I suppose it just can just be any filler class that's empty. It's serialized correctly.
Your problem is most likely this line:
fs = File.OpenWrite(fullSavePath);
If the previous file was longer than the new one, you will still have fragments from the previous save file at the end - that's why you can't deserialize it. Get rid of everything in that if condition except for the File.Create and it should work.
I'm relatively new to c# and I am trying to write a program that finds the mean of every xth value in a file using Streamreader. (For example if I wanted to find the mean of every fifth value in that file)
I written some code that reads the file and splits it into a new line for each comma, and this works fine, when I try and read each specific value.
However I'm struggling to think of a way to find every specific value, such as every 4th one and then find the mean of these and output it in the same program.
static void Main(string[] args)
{
using (var reader = new StreamReader(#"file"))
{
List<string> list = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
list.Add(values[0]);
}
}
}
Any suggestions or help would be greatly appreciated
Try like this;
static void Main()
{
using (var reader = new StreamReader(#"file"))
{
int lineNumber = 4;
bool streamEnded = false;
List<string> list = new List<string>();
while (!streamEnded)
{
var line = ReadSpecificLine(reader, lineNumber,out streamEnded);
if (string.IsNullOrEmpty(line))
{
continue;
}
var values = line.Split(',');
list.Add(values[0]);
}
}
}
public static string ReadSpecificLine(StreamReader sr, int lineNumber,out bool streamEnded)
{
streamEnded = false;
for (int i = 1; i < lineNumber; i++)
{
if (sr.EndOfStream)
{
streamEnded = true;
return "";
}
sr.ReadLine();
}
if (sr.EndOfStream)
{
streamEnded = true;
return "";
}
return sr.ReadLine();
}