AppendAllText producing invalid JSON - c#

I have a JSON file containing:
[{
"title":"Colors",
"text":"1. White 2. Blue 3. Red 4. Yellow 5. Green"
}]
If I use
string json = JsonConvert.SerializeObject(favecolors, Formatting.Indented);
var jsonFile= Server.MapPath("~/App_Data/favecolors.json");
System.IO.File.AppendAllText(#jsonFile, ","+json);
I can append a JSON object to the file resulting in:
[{
"title":"Colors",
"text":"1. White 2. Blue 3. Red 4. Yellow 5. Green"
}],{
"title":"Colors",
"text":"1. White 2. Blue 3. Red 4. Yellow 5. Green"
}
which isn't valid JSON because the right square bracket is in the wrong place. Can anybody help?

If the format of the json of your file is going to be always the same, in that case a json array with at least one node [{},{}] you could do that
var jsonFile = Server.MapPath("~/App_Data/favecolors.json");
FileStream fs = new FileStream(#jsonFile, FileMode.Open, FileAccess.ReadWrite);
fs.SetLength(fs.Length - 1); // Remove the last symbol ']'
fs.Close();
string json = JsonConvert.SerializeObject(favecolors, Formatting.Indented);
System.IO.File.AppendAllText(#jsonFile, "," + json + "]");
It is not the most elegant solution but it should make that you want to do.
Note: this solution is very tricky, be sure that the content of your file ends with the ']' symbol, otherwise you would rather do the following: 1) Read the file into a var, 2) concat the new json (fitting it properly), 3) write the file with the merged json.

You could try something more generic, instead of adding/removing the ending bracket. Hope the following example fits your needs.
[JsonObject]
public class FavoriteColor
{
public FavoriteColor()
{}
public FavoriteColor(string title, string text)
{
Title = title;
Text = text;
}
[JsonProperty(PropertyName = "title")]
public string Title { get; set; }
[JsonProperty(PropertyName = "text")]
public string Text { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Append new objects to your file
private async Task Append()
{
// Read file content and deserialize
string content = await ReadAsync("json.txt");
var colors = new List<FavoriteColor>();
if (!string.IsNullOrWhiteSpace(content))
colors.AddRange(JsonConvert.DeserializeObject<List<FavoriteColor>>(content));
// Add your new favorite color!
var fav = new FavoriteColor("new", "new color");
colors.Add(fav);
// Writo back to file
await WriteAsync("json.txt", JsonConvert.SerializeObject(colors));
}
// Async read
private async Task<string> ReadAsync(string file)
{
if (!File.Exists(file))
return null;
string content;
using (var fileStream = File.OpenRead(file))
{
byte[] buffer = new byte[fileStream.Length];
await fileStream.ReadAsync(buffer, 0, (int)fileStream.Length);
content = Encoding.UTF8.GetString(buffer);
}
return content;
}
// Async write
private async Task WriteAsync(string file, string content)
{
using (var fileStream = File.OpenWrite(file))
{
byte[] buffer = (new UTF8Encoding()).GetBytes(content);
await fileStream.WriteAsync(buffer, 0, buffer.Length);
}
}
}

Related

Is there a recommended way of writing the contents of variables to a text file?

As a tutorial project I had to create a Coffee Machine Simulator using c#. I have completed this project successfully, but I would like the contents of the variables to be written to a file so that the user does not need to set it up again. I have tried this by attempting the demo project from Microsoft:
using System;
using System.Linq;
using System.IO;
using System.Reflection.Metadata;
using System.Text;
namespace TestingCode
{
class Program
{
public static void Main()
{
string path = "Test.txt";
try
{
// Create the file, or overwrite if the file exists.
using (FileStream fs = File.Create(path))
{
Console.WriteLine("Enter a string:");
string input = Console.ReadLine();
byte[] info = new UTF8Encoding(true).GetBytes(input);
// Add some information to the file.
fs.Write(info, 0, info.Length);
}
// Open the stream and read it back.
using (StreamReader sr = File.OpenText(path))
{
string s = "";
while ((s = sr.ReadLine()) != null)
{
Console.WriteLine(s);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.ReadLine();
}
}
}
This code successfully runs and writes the value of the user input to a text file. Please can anybody help me write the variables into a text file and read from them as well.
Thanks,
KINGAWESOME266
Edit: you can install Newtonsoft.Json by using nugget package manager
The fastest and easiest way I can think of is using Newtonsoft.Json to serialize data
Create a class to store your variables:
Let's say we have this Model class which stores our variables
public class Model
{
public int Variable1;
public string Variable2;
public List<string> Variable3;
}
Here is our object that we want to serialize:
Model m = new Model()
{
Variable1 = 1,
Variable2 = "test test",
Variable3 = new List<string>() { "list element 1 ", "list element 2", "list element 3"}
};
To serialize this object call JsonConvert.SerializeObject with your object as param
The output of this is a json string, in our case:
{"Variable1":1,"Variable2":"test test","Variable3":["list element 1 ","list element 2","list element 3"]}
var serializedData = JsonConvert.SerializeObject(m);
Save the string to a file and read it back again
File.WriteAllText("serialized.txt", serializedData);
var loadedData = File.ReadAllText("serialized.txt");
Convert back the read string to an object by calling JsonConvert.DeserializeObject(yourObject);
var loadedObject = JsonConvert.DeserializeObject<Model>(loadedData);
Complete example:
static void Main(string[] args)
{
Model m = new Model()
{
Variable1 = 1,
Variable2 = "test test",
Variable3 = new List<string>() { "list element 1 ", "list element 2", "list element 3" }
};
var serializedData = JsonConvert.SerializeObject(m);
File.WriteAllText("serialized.txt", serializedData);
var loadedData = File.ReadAllText("serialized.txt");
var loadedObject = JsonConvert.DeserializeObject<Model>(loadedData);
}

Better way of writing json files in C# and validate the code

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>

How to serialize a list of objects with JSON

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;
}

C# Pdf to Text with image placeholder

I have a batch of PDFs that I want to convert to Text. It's easy to get text with something like this from iTextSharp:
PdfTextExtractor.GetTextFromPage(reader, pageNumber);
It's easy to get Images using this answer (or similar answers in the thread).
What I can't figure out easily... is how to interleave image placeholders in the text.
Given a PDF, a page # and GetTextFromPage I expect the output to be:
line 1
line 2
line 3
When I'd like it to be (Where 1.1 means page 1, image 1... Page 1, image 2):
line 1
[1.1]
line 2
[1.2]
line 3
Is there a way to get an "image placeholder" for iTextSharp, PdfSharp or anything similar? I'd like a GetTextAndPlaceHoldersFromPage method (or similar).
PS: Hrm... it's not letting me tag iTextSHARP - not iText. C# not Java.
C# Pdf to Text with image placeholder
https://stackoverflow.com/a/28087521/
https://stackoverflow.com/a/33697745/
Although this doesn't have the exact layout mentioned in my question (Since that was a simplified version of what I really wanted anyways), it does have the starting parts as listed by the second note (translated from iText Java)... with extra information pulled from the third note (Some of the reflection used in Java didn't seem to work in C#, so that info came from #3).
Working from this, I'm able to get a List of Strings representing lines in the PDF (all pages, instead of just page 1)... with text added where images should be (Huzzah!). ByteArrayToFile extension method added for flavor (Although I didn't include other parts/extensions that may break a copy/paste usages of this code).
I've also been able to greatly simplify other parts of my process and gut half of the garbage I had working before. Huzzah!!! Thanks #Mkl
internal class Program
{
public static void Main(string[] args)
{
var dir = Settings.TestDirectory;
var file = Settings.TestFile;
Log.Info($"File to Process: {file.FullName}");
using (var reader = new PdfReader(file.FullName))
{
var parser = new PdfReaderContentParser(reader);
var listener = new SimpleMixedExtractionStrategy(file, dir);
parser.ProcessContent(1, listener);
var x = listener.GetResultantText().Split('\n');
}
}
}
public class SimpleMixedExtractionStrategy : LocationTextExtractionStrategy
{
public static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public DirectoryInfo OutputPath { get; }
public FileInfo OutputFile { get; }
private static readonly LineSegment UNIT_LINE = new LineSegment(new Vector(0, 0, 1), new Vector(1, 0, 1));
private int _counter;
public SimpleMixedExtractionStrategy(FileInfo outputFile, DirectoryInfo outputPath)
{
OutputPath = outputPath;
OutputFile = outputFile;
}
public override void RenderImage(ImageRenderInfo renderInfo)
{
try
{
var image = renderInfo.GetImage();
if (image == null) return;
var number = _counter++;
var imageFile = new FileInfo($"{OutputFile.FullName}-{number}.{image.GetFileType()}");
imageFile.ByteArrayToFile(image.GetImageAsBytes());
var segment = UNIT_LINE.TransformBy(renderInfo.GetImageCTM());
var location = new TextChunk("[" + imageFile + "]", segment.GetStartPoint(), segment.GetEndPoint(), 0f);
var locationalResultField = typeof(LocationTextExtractionStrategy).GetField("locationalResult", BindingFlags.NonPublic | BindingFlags.Instance);
var LocationalResults = (List<TextChunk>)locationalResultField.GetValue(this);
LocationalResults.Add(location);
}
catch (Exception ex)
{
Log.Debug($"{ex.Message}");
Log.Verbose($"{ex.StackTrace}");
}
}
}
public static class ByteArrayExtensions
{
public static bool ByteArrayToFile(this FileInfo fileName, byte[] byteArray)
{
try
{
// Open file for reading
var fileStream = new FileStream(fileName.FullName, FileMode.Create, FileAccess.Write);
// Writes a block of bytes to this stream using data from a byte array.
fileStream.Write(byteArray, 0, byteArray.Length);
// close file stream
fileStream.Close();
return true;
}
catch (Exception exception)
{
// Error
Log.Error($"Exception caught in process: {exception.Message}", exception);
}
// error occured, return false
return false;
}
}

Is DataGridView wrong choice?

I am new to C# and Windows Forms so please be patient. I am making a Windows Form that lets you load a video, load a script, then makes captions. I want to display a table where the data will be populated automatically as they mark starts and ends of caption and select text and will be editable. At the end I want to save all the data to an xml file. A DataGridView UI-wise seems like exactly what I want, but I can't figure out backend how to get the data out of the DataGridView ( preferably in a dataset).
I am now considering using a ListView with multiple columns instead. Any advice would be greatly appreciated
Datagridview should work just fine for that application and you can easily retrieve any data you save to it by doing:
dgvThing.DataSource;
That will contain whatever type that you have saved into the Datagridview (List, array, etc).
Example:
public class SuperFunObject {
public TimeSpan start { get; set; }
public TimeSpan end { get; set; }
public string selectedText { get; set; }
public SuperFunObject(Timespan a, Timespan b, string text) {
start = a;
end = b;
selectedText = text;
}
}
List<SuperFunObject> funList = new List<SuperFunObject>();
funList.Add(new SuperFunObject(TimeSpan.FromSeconds(0.0),TimeSpan.FromSeconds(20.0),"Hello"));
dgvThing.DataSource = funList;
...
...
//retrive your list
List<SuperFunObject> getData = ((List<SuperFunObject>)dgvThing.DataSource);
I hope the example helps a bit. Side note, the reason for the accessors (get,set) are for the Datagridview to be able to retrieve the data from the object for display.
Here is a little ditty that'll save a class that you populate into an array from your datasource to am XML file path that you specify in the parameter.
public static bool SaveXMLObjectToFile(object IncomingXMLObject, string Path)
{
string xmlString = null;
File TheFileIn = default(File);
string docname = null;
StreamWriter WriteAFile = default(StreamWriter);
string filelocation = null;
//Dim filelocation As String
System.IO.MemoryStream MemStream = new System.IO.MemoryStream();
System.Xml.Serialization.XmlSerializer Ser = default(System.Xml.Serialization.XmlSerializer);
System.Text.Encoding encodingvalue = System.Text.UTF8Encoding.UTF8;
System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(MemStream, encodingvalue);
bool Result = false;
try {
File.Delete(Path);
Ser = new System.Xml.Serialization.XmlSerializer(IncomingXMLObject.GetType);
Ser.Serialize(writer, IncomingXMLObject);
MemStream = writer.BaseStream;
//as system.io.memorystream
xmlString = UTF8ByteArrayToString(MemStream.ToArray());
//Will Not Convert Byte Array from Diagram
filelocation = Path;
WriteAFile = TheFileIn.AppendText(filelocation);
WriteAFile.Write(xmlString);
WriteAFile.Close();
Result = true;
} catch (Exception e) {
Result = false;
}
return Result;
}

Categories

Resources