How to append node to existing json file using json.net - c#

i am working with json in asp.net using json.NET where on button click values from textbox gets added to json file called country.json. There are two textbox which takes country and its capital as values,
country.json file looks like this,
[
{
"country":"USA",
"capital":"New York"
},
{
"country":"China",
"capital":"Bejing"
},
{
"country":"India",
"capital":"New Delhi"
}
]
i was able to create json with one node but how to append or add second node to existing json.
Here is the c# code ,
public class country
{
public string Country { get; set; }
public string Capital { get; set; }
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
country ctry = new country();
ctry.Country = txtCtry.Text;
ctry.Capital = txtCapital.Text;
File.AppendAllText(MapPath("Data/countrycaps.json"),JsonConvert.SerializeObject(ctry,Formatting.Indented));
}

If you want a list, you should be saving a list, not a single node.
Here are the steps:
If file exists, load all nodes from existing file into list.
Add new node when user provides data.
Save list to file.

I needed the same feature and the round-trip was just too expensive.
This is what I came up with:
private static void AppendTransaction(Transaction transaction)
{
const string filename = "transactions.json";
bool firstTransaction = !File.Exists(filename);
JsonSerializer ser = new JsonSerializer();
ser.Formatting = Formatting.Indented;
ser.TypeNameHandling = TypeNameHandling.Auto;
using (var fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read))
{
Encoding enc = firstTransaction ? new UTF8Encoding(true) : new UTF8Encoding(false);
using (var sw = new StreamWriter(fs, enc))
using (var jtw = new JsonTextWriter(sw))
{
if (firstTransaction)
{
sw.Write("[");
sw.Flush();
}
else
{
fs.Seek(-Encoding.UTF8.GetByteCount("]"), SeekOrigin.End);
sw.Write(",");
sw.Flush();
}
ser.Serialize(jtw, transaction);
sw.Write(']');
}
}
}

Related

How to read and write a list of users in a text file

Hello I'm trying to learn C# and although I have looked I haven't found a way of solving my problem.
I have a Users class with a Username, Password, Score.
And all of them have setters and getters.
What I'm trying to do is:
After I create my List : ListOfUsers I wanna save all of the users of that list in a single text file (Login.txt)
I tried this:
Users user = new Users(username, password, score);
ListOfUsers.Add(user);
StreamWriter sw = new StreamWriter("Login.txt", true);
sw.WriteLine(ListOfUsers);
sw.Close();
I think that I have passed the list into my file. But I can't seem to find a way of reading all the users from the file.
Thank you for your time!
If you want to save to a text file, at least make it an Xml file, instead of a flat file. The code below adds users to a list, saves it as an Xml, clears the list, and then reads from the file.
The Xml file looks like this:
<ArrayOfUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<User Name="A" Password="AAA" Score="100" />
<User Name="B" Password="BBB" Score="87" />
<User Name="C" Password="CCC" Score="52" />
</ArrayOfUser>
And the sample source code
public class User
{
public User()
{
this.Name= string.Empty;
this.Password = string.Empty;
this.Score= 0;
}
public User(string name, string password, int score)
{
Name=name;
Password=password;
Score=score;
}
[XmlAttribute] public string Name { get; set; }
[XmlAttribute] public string Password { get; set; }
[XmlAttribute] public int Score { get; set; }
}
class Program
{
static void Main(string[] args)
{
var list = new List<User>();
list.Add(new User("A", "AAA", 100));
list.Add(new User("B", "BBB", 87));
list.Add(new User("C", "CCC", 52));
SaveToXml(list.ToArray(), "Login.xml");
list.Clear();
list.AddRange(ReadFromXml("Login.xml"));
foreach (var item in list)
{
Console.WriteLine($"{item.Name}, {item.Score}");
}
}
static void SaveToXml(User[] users, string fileName)
{
var xs = new XmlSerializer(typeof(User[]));
var settings = new XmlWriterSettings()
{
Indent = true,
OmitXmlDeclaration = true,
};
var xw = XmlWriter.Create(fileName, settings);
xs.Serialize(xw, users);
xw.Close();
}
static User[] ReadFromXml(string fileName)
{
var xs = new XmlSerializer(typeof(User[]));
var xr = XmlReader.Create(fileName);
return xs.Deserialize(xr) as User[];
}
}
As Vladimir told, better don't save any login data to a text file (even better don't save it at all).
In any way, if it's something you have to do for any reason, you can look here: here
After that, I suggest you to encrypt the file, so nobody can read the content except the user that creates the file itself: How to crypt/decrypt file
I did something like that, some time ago. Here's the example.
SaveFileDialog saveFileDialog = new SaveFileDialog();
saveFileDialog.Filter = "Archivo de texto (*.txt)|*.txt";
saveFileDialog.FileName = $"Compras_Auxiliar_{(int)Periodo.Value}{_ejercicioContableManager.SearchById(_idEjercicioContable).Anio}_{_empresaManager.SearchById(_idEmpresa).Nit}.txt";
saveFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
List<BancarizacionCompra> listaBancarizacionCompra = _bancarizacionComprasManager.ObtenerTodo.Where(bancarizacion => bancarizacion.EsProyecto == false).ToList();
string[] lines = new string[listaBancarizacionCompra.Count()];
for (int i = 0; i < listaBancarizacionCompra.Count(); i++)
{
lines[i] = $"{(int)listaBancarizacionCompra[i].ModalidadTransaccion}|{listaBancarizacionCompra[i].FechaDocumentoPago.ToShortDateString()}|{(int)listaBancarizacionCompra[i].TipoTransaccion}|{listaBancarizacionCompra[i].CiNitProveedor}|{listaBancarizacionCompra[i].NombreRazonSocialProveedor}|{listaBancarizacionCompra[i].NombreRazonSocialProveedor}|{listaBancarizacionCompra[i].NumeroFactura}|{listaBancarizacionCompra[i].NumeroContrato}|{listaBancarizacionCompra[i].ImporteFacturaDocumento.ToString("c").Replace(".", "").Replace(",", ".")}|{listaBancarizacionCompra[i].NumAutorizacionFacturaDocumento}|{listaBancarizacionCompra[i].NumCuentaDocumentoPago}|{listaBancarizacionCompra[i].MontoPagadoDocumentoPago.ToString("c").Replace(".", "").Replace(",", ".")}|{listaBancarizacionCompra[i].MontoAcumulado.ToString("c").Replace(".", "").Replace(",", ".")}|{listaBancarizacionCompra[i].NitEntidadFinanciera}|{listaBancarizacionCompra[i].NumDocumentoPago}|{(int)listaBancarizacionCompra[i].TipoDocumentoPago}|{listaBancarizacionCompra[i].FechaDocumentoPago.ToShortDateString()}";
}
File.WriteAllLines(saveFileDialog.FileName, lines);
Mensajes.CrearMensajeInformativo("Exportación exitosa");
if (Mensajes.CrearMensajeInterrogacion("¿Desea abrir el archivo?"))
{
Process.Start(saveFileDialog.FileName);
}
}
With the expectation that you are using dotnet 3.1 or higher you can use:
Option 1
Using just the File class.
// System.Text.Json serializer
await File.WriteAllTextAsync("Login.txt", JsonSerializer.Serialize(ListOfUsers));
Option 2
The other option without File is to use a StreamWriter instead.
using (var sw = new StreamWriter("Login.txt", true))
{
// System.Text.Json serializer
await sw.WriteLineAsync(JsonSerializer.Serialize(ListOfUsers));
}
Both will at the end produce the "Login.txt" file with a JSON format of the users list.
In case you are using a lower version than dotnet 3.1 you need to include Newtonsoft.Json which provides another JSON serializer:
// Newtonsoft serializer
JsonConvert.SerializeObject(ListOfUsers);
Important: Be aware (like written in the comments and other answers) this data is not encrypted and should not be written in that way.

Write a text file with tab delimiter in .Net Core

Hi do you have any guides, work aid or step by step how to export to text with tab delimited. Im using Asp.Net Core 2.2 MVC EF. I want to export a list from my
table.. I want to have a button where the user click in this DownloadFile Action will trigger.
public object DownloadFile()
{
var payments = new List<BdoPE>
{
new BdoPE
{
DocDateInDoc = "01/01/2019",
DocType = "DZ",
CompanyCode = "3000",
PosDateInDoc = "01/01/2019",
FiscalPeriod = "01",
CurrentKey = "PHP",
RefDocNum = "Over-The-Counter",
DocHeadT = "BDO",
PosKeyInNextLine = "40",
AccMatNextLine = "11231131",
AmountDocCur = "0000000010050",
ValDate = "01/01/2019",
AssignNum = "EEA",
ItemText = "1000136212 ",
PosKeyInNextLine2 = "15",
AccMatNextLine2 = "0115027FF",
AmountDocCur2 = "0000000010050",
BaseDateDueCal = "01/01/2019",
ItemText2 = "1000136212"
},
};
// I want this part to let the user select where they want to save the text file.
using (var writer = new StreamWriter("path\\to\\file.txt")) // not static location like this one.
using (var csv = new CsvWriter(writer))
{
csv.WriteHeader<BdoPE>();
csv.WriteRecord(payments);
}
// where should i put the delimiter part?
return; // to what?
}
You should use this package https://joshclose.github.io/CsvHelper/
You can easily write a text file with tab delimiter.
using CsvHelper;
using System;
using System.Collections.Generic;
using System.IO;
namespace ConsoleApp5
{
class Program
{
static void Main(string[] args)
{
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
};
var configuration = new CsvHelper.Configuration.Configuration()
{
Delimiter = "\t"
};
using (var writer = new StreamWriter("path\\to\\file.csv"))
using (var csv = new CsvWriter(writer, configuration))
{
csv.WriteRecords(records);
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
}
}

Inserting json documents in DocumentDB

In DocumentDB documentation examples, I find insertion of C# objects.
// Create the Andersen family document.
Family AndersenFamily = new Family
{
Id = "AndersenFamily",
LastName = "Andersen",
Parents = new Parent[] {
new Parent { FirstName = "Thomas" },
new Parent { FirstName = "Mary Kay"}
},
IsRegistered = true
};
await client.CreateDocumentAsync(documentCollection.DocumentsLink, AndersenFamily);
In my case, I'm receiving json strings from application client and would like to insert them in DocumentDB without deserializing them. Could not find any examples of doing something similar.
Any help is sincerely appreciated..
Thanks
Copied from the published .NET Sample code -
private static async Task UseStreams(string colSelfLink)
{
var dir = new DirectoryInfo(#".\Data");
var files = dir.EnumerateFiles("*.json");
foreach (var file in files)
{
using (var fileStream = new FileStream(file.FullName, FileMode.Open, FileAccess.Read))
{
Document doc = await client.CreateDocumentAsync(colSelfLink, Resource.LoadFrom<Document>(fileStream));
Console.WriteLine("Created Document: ", doc);
}
}
//Read one the documents created above directly in to a Json string
Document readDoc = client.CreateDocumentQuery(colSelfLink).Where(d => d.Id == "JSON1").AsEnumerable().First();
string content = JsonConvert.SerializeObject(readDoc);
//Update a document with some Json text,
//Here we're replacing a previously created document with some new text and even introudcing a new Property, Status=Cancelled
using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes("{\"id\": \"JSON1\",\"PurchaseOrderNumber\": \"PO18009186470\",\"Status\": \"Cancelled\"}")))
{
await client.ReplaceDocumentAsync(readDoc.SelfLink, Resource.LoadFrom<Document>(memoryStream));
}
}

Error while deserializing to json an unzipped string

I create a zip file and I copy in it a file that contains a serialized list of objects. The file encoding is in UTF8. Then I unzip the file and I try to deserialize it, but I will get this error:
Unexpected character encountered while parsing value: . Path '', line 0, position 0
The problem does not exist if I use ASCII encoding instead of UTF8. But I need to use the UTF8. So I am wondering if the DotNetZip library does not have full support for the UTF8, or maybe I am missing something else.
In order to reproduce the error:
Json library is the one at: http://json.codeplex.com/
The Zip library is the one at: http://dotnetzip.codeplex.com/
Create a simple class "Dog":
public class Dog
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Then use this code (the last line will cause the error):
var list = new List<Dog>();
list.Add(new Dog { FirstName = "Arasd", LastName = "1234123" });
list.Add(new Dog { FirstName = "fghfgh", LastName = "vbnvbn" });
var serialized = JsonConvert.SerializeObject(list, Formatting.Indented);
var zipFile = new ZipFile(#"C:\Users\daviko\Desktop\test.zip");
using (zipFile)
{
zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zipFile.UpdateEntry("dogs.txt", serialized, UTF8Encoding.UTF8);
zipFile.Save();
}
var readFromZipFile = string.Empty;
using (var input = new MemoryStream())
{
using (zipFile)
{
var entry = zipFile["dogs.txt"];
entry.Extract(input);
}
using (var output = new MemoryStream())
{
input.CopyTo(output);
readFromZipFile = new UTF8Encoding().GetString( input.ToArray());
}
}
var deserialized = JsonConvert.DeserializeObject<List<Dog>>(readFromZipFile);
The following code:
using (zipFile)
{
zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.BestCompression;
zipFile.UpdateEntry("dogs.txt", serialized, UTF8Encoding.UTF8);
zipFile.Save();
}
will dispose the zipFile when it executes. So you must create the zipFile again, before your try reading it again.

How to append data in a serialized file on disk

I have a program written in C# that serializes data into binary and write it on the disk. If I want to add more data to this file, fist I have to deserialise whole file and then append more serialized data to it. Is it possible to append data to this serialized file without deserialising the existing data so that I can save some time during whole process?
You don't have to have to read all the data in the file to append data.
You can open it in append mode and write the data.
var fileStream = File.Open(fileName, FileMode.Append, FileAccess.Write, FileShare.Read);
var binaryWriter = new BinaryWriter(fileStream);
binaryWriter.Write(data);
Now that we know (comments) that we're talking about a DataTable/DataSet via BinaryFormatter, it becomes clearer. If your intention is for that to appear as extra rows in the existing table, then no: that isn't going to work. What you could do is append, but deserialize each table in turn, then manually merge the contents. That is probably your best bet with what you describe. Here's an example just using 2, but obviously you'd repeat the deserialize/merge until EOF:
var dt = new DataTable();
dt.Columns.Add("foo", typeof (int));
dt.Columns.Add("bar", typeof(string));
dt.RemotingFormat = SerializationFormat.Binary;
var ser = new BinaryFormatter();
using(var ms = new MemoryStream())
{
dt.Rows.Add(123, "abc");
ser.Serialize(ms, dt); // batch 1
dt.Rows.Clear();
dt.Rows.Add(456, "def");
ser.Serialize(ms, dt); // batch 2
ms.Position = 0;
var table1 = (DataTable) ser.Deserialize(ms);
// the following is the merge loop that you'd repeat until EOF
var table2 = (DataTable) ser.Deserialize(ms);
foreach(DataRow row in table2.Rows) {
table1.ImportRow(row);
}
// show the results
foreach(DataRow row in table1.Rows)
{
Console.WriteLine("{0}, {1}", row[0], row[1]);
}
}
However! Personally I have misgivings about both DataTable and BinaryFormatter. If you know what your data is, there are other techniques. For example, this could be done very simply with "protobuf", since protobuf is inherently appendable. In fact, you need to do extra to not append (although that is simple enough too):
[ProtoContract]
class Foo
{
[ProtoMember(1)]
public int X { get; set; }
[ProtoMember(2)]
public string Y { get; set; }
}
[ProtoContract]
class MyData
{
private readonly List<Foo> items = new List<Foo>();
[ProtoMember(1)]
public List<Foo> Items { get { return items; } }
}
then:
var batch1 = new MyData { Items = { new Foo { X = 123, Y = "abc" } } };
var batch2 = new MyData { Items = { new Foo { X = 456, Y = "def" } } };
using(var ms = new MemoryStream())
{
Serializer.Serialize(ms, batch1);
Serializer.Serialize(ms, batch2);
ms.Position = 0;
var merged = Serializer.Deserialize<MyData>(ms);
foreach(var row in merged.Items) {
Console.WriteLine("{0}, {1}", row.X, row.Y);
}
}

Categories