How do I parse only value in json file - c#

From this code. I want to parse only value from the json file
if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
using (StreamReader file = File.OpenText(openFileDialog1.FileName))
using (JsonTextReader reader = new JsonTextReader(file))
{
while (reader.Read())
{
if (reader.Value != null)
{
richTextBox1.Text = reader.Value.ToString();
}
else
{
MessageBox.Show("Error while parsing json file. Please try again.");
}
}
}
}
And the value is
{
"install.and": "a",
"install.emailAddress": "E-mailová adresa",
"install.emailIncorrect": "Zadejte platnou e-mailovou adresu.",
"install.emailRetryPrefix": "Neobdrželi jste e-mail? Zkuste to znovu",
"install.emailRetry": "Zkuste to znovu",
"install.emailSend": "Odeslat odkaz",
"install.emailSent": "E-mail byl odeslán!",
"install.emailSentTo": "E-mail byl odeslán",
"install.emailText1": "Můžete navštívit",
"install.emailText2": "Pokud nám poskytnete e-mailovou adresu, budeme vám moci poslat odkaz na pozdější instalaci.",
"install.installing": "Instalace...",
"install.later": "Instalovat později",
"install.licenseAgreement": "licenční smlouva",
"install.privacyPolicy": "zásady ochrany osobních údajů",
"install.quit": "Ukončit instalační program"
}
I want to parse it after : symbol. (Is it value?) to show in richTextbox as Text.

Try this code
using (StreamReader file = File.OpenText(openFileDialog1.FileName))
using (JsonTextReader reader = new JsonTextReader(file))
{
var o = JObject.Load(reader);
foreach (var v in o)
{
var value = v.Value.Value<string>();
//do whatever you want with value
}
}
If you want only values joined by newline, then try this one
using (StreamReader file = File.OpenText(openFileDialog1.FileName))
using (JsonTextReader reader = new JsonTextReader(file))
{
var o = JObject.Load(reader);
var e = o.Values().Select(x => x.Value<string>());
var values = string.Join(Environment.NewLine, e);
//do whatever you want with values
}

Introduce two temporary variable to hold key and value
string key = string.Empty;
string value = string.Empty;
Modify your while loop like this,
using (JsonTextReader reader = new JsonTextReader(file))
{
while (reader.Read())
{
if (reader.Value != null)
{
key = reader.Value.ToString();
if (reader.Read())
value = reader.Value.ToString();
Console.WriteLine("{0} : {1}", key,value);
//Instead of writing in a console, process and write it in Rich text box.
}
}
}

You can use Json.Net and create a model :
public class JsonObject
{
[JsonProperty("install.and")]
public string install_and { get; set; }
[JsonProperty("install.emailAddress")]
public string emailAddress { get; set; }
[JsonProperty("install.emailIncorrect")]
public string emailIncorrect { get; set; }
[JsonProperty("emailRetryPrefix")]
public string emailRetryPrefix { get; set; }
[JsonProperty("install.emailRetry")]
public string emailRetry { get; set; }
[JsonProperty("install.emailSend")]
public string emailSend { get; set; }
[JsonProperty("install.emailSent")]
public string emailSent { get; set; }
[JsonProperty("install.emailSentTo")]
public string emailSentTo { get; set; }
[JsonProperty("install.emailText1")]
public string emailText1 { get; set; }
[JsonProperty("install.emailText2")]
public string emailText2 { get; set; }
[JsonProperty("install.installing")]
public string installing { get; set; }
[JsonProperty("install.later")]
public string later { get; set; }
[JsonProperty("install.licenseAgreement")]
public string licenseAgreement { get; set; }
[JsonProperty("install.privacyPolicy")]
public string privacyPolicy { get; set; }
[JsonProperty("install.quit")]
public string quit { get; set; }
}
Then you can prase you json file:
string json_data = "{\"install.and\": \"a\",\"install.emailAddress\": \"E-mailová adresa\",\"install.emailIncorrect\": \"Zadejte platnou e-mailovou adresu.\",\"install.emailRetryPrefix\": \"Neobdrželi jste e-mail? Zkuste to znovu\",\"install.emailRetry\": \"Zkuste to znovu\",\"install.emailSend\": \"Odeslat odkaz\",\"install.emailSent\": \"E-mail byl odeslán!\",\"install.emailSentTo\": \"E-mail byl odeslán\",\"install.emailText1\": \"Můžete navštívit\",\"install.emailText2\": \"Pokud nám poskytnete e-mailovou adresu, budeme vám moci poslat odkaz na pozdější instalaci.\",\"install.installing\": \"Instalace...\",\"install.later\": \"Instalovat později\",\"install.licenseAgreement\": \"licenční smlouva\",\"install.privacyPolicy\": \"zásady ochrany osobních údajů\",\"install.quit\": \"Ukončit instalační program\"";
JsonObject data = JsonConvert.DeserializeObject<JsonObject>(json_data);
richTextBox1.Text = data.emailAddress;
richTextBox2.Text = data.emailIncorrect;
richTextBox3.Text = data.emailRetry;
[...]

First, Install newtonsoft.json from nuget package manager. Add the namespace
using Newtonsoft.Json.Linq;
create a class to easily handle the values.
class Details
{
public string and;
public string EmailAddress;
public string EmailIncorrect;
public string EmailRetry;
public string EmailSend;
public string EmailSent;
}
Then read the file into a string and parse it using JObject.
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
string file = File.ReadAllText(openFileDialog1.FileName);
JObject jo = JObject.Parse(file);
Details dt = new Details();
dt.and = (string)jo["install.and"];
richTextBox1.Text = reader.Value.ToString();
}

Related

System.InvalidOperationException: 'There is an error in the XML document (1, 1).'

This error is showing up in my code, there is a second one that is as follows:
XmlException: The existing data at the root level is invalid. Line 1, position 1
I checked this second one saying there is a error with the file when there isn't any since I have 5 files inside my XMLFiles directory.
public static void Main()
{
XmlSerializer serializer = new XmlSerializer(typeof(ImportSession));
MemoryStream stream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(stream))
{
sw.Write(stream);
sw.Flush();
stream.Position = 0;
}
Console.ReadKey();
foreach (string filename in Directory.EnumerateFiles(#"C:\XMLFiles", "*.xml"))
{
ProcessFile(filename, stream, serializer);
}
void ProcessFile(string Filename, MemoryStream stream, XmlSerializer serializer)
{
bool temErro = false;
Console.WriteLine("A processar xml: " + Filename);
XmlDocument xml = new XmlDocument();
xml.Load(Filename);
ImportSession session = (ImportSession)serializer.Deserialize(stream);
foreach (Batch batch in session.Batches)
{
foreach (Document doc in batch.Documents)
{
foreach (Page page in doc.Pages)
{
if (!string.IsNullOrEmpty(batch.Processed.ToString()))
{
if (!string.IsNullOrEmpty(page.HasError.ToString()))
{
string Import = page.ImportFileName;
Console.WriteLine("Página com erro:" + Import);
temErro = true;
}
}
}
}
}
if (temErro)
Console.WriteLine("Ficheiro com erro: " + Filename);
else
Console.WriteLine("Ficheiro processado: " + Filename);
Console.WriteLine(Filename);
}
}
public class ImportSession
{
public Batch[] Batches { get; set; }
}
public class Batch
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Description { get; set; }
[XmlAttribute]
public string BatchClassName { get; set; }
[XmlAttribute]
public bool Processed { get; set; }
public Document[] Documents { get; set; }
}
public class Document
{
[XmlAttribute]
public string FormTypeName { get; set; }
public IndexField[] IndexFields { get; set; }
public Page[] Pages { get; set; }
}
public class IndexField
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Value { get; set; }
}
public class Page
{
[XmlAttribute]
public string ImportFileName { get; set; }
[XmlAttribute]
public string ErrorCode { get; set; }
[XmlAttribute]
public string ErrorMessage { get; set; }
[XmlIgnore]
public bool HasError => !string.IsNullOrWhiteSpace(ErrorMessage);
}
This app right now is only trying to read all the files and point out some parts that need to show up in the console and it was doing it but I was adviced on here to change into this object oriented and memory stream.
This:
MemoryStream stream = new MemoryStream();
using (StreamWriter sw = new StreamWriter(stream))
{
sw.Write(stream);
sw.Flush();
stream.Position = 0;
is basically meaningless. Whatever the contents of stream are meant to be: it isn't this. Ask yourself:
What is stream meant to contain?
At the moment it contains... itself, sort of, but not really?
If you intend the stream to be the file contents: just use File.OpenRead
I think this is based on a misunderstanding from answers to previous questions on the topic.
This should make it work. BUT keep in mind, that it is in no way production-ready.
public static void Main()
{
XmlSerializer serializer = new XmlSerializer(typeof(ImportSession));
foreach (string filename in Directory.EnumerateFiles(#"C:\XMLFiles", "*.xml"))
{
ProcessFile(filename, serializer);
}
Console.ReadKey();
}
private static void ProcessFile(string Filename, XmlSerializer serializer)
{
bool temErro = false;
Console.WriteLine("A processar xml: " + Filename);
using (var file = File.OpenRead(Filename)) {
var session = (ImportSession)serializer.Deserialize(file);
// from here on the rest of your code ...
To minimize the code that keeps the file opened:
ImportSession session;
using (var file = File.OpenRead(Filename))
{
session = (ImportSession)serializer.Deserialize(file);
}
// file will be closed by disposal of FileStream using this notation
// rest of code
Addendum
if (!string.IsNullOrEmpty(batch.Processed.ToString()))
{ // Will ALWAYS be entered!
if (!string.IsNullOrEmpty(page.HasError.ToString()))
{ // Will ALWAYS be entered!
string Import = page.ImportFileName;
Console.WriteLine("Página com erro:" + Import);
temErro = true;
}
}
Let's look at it:
!string.IsNullOrEmpty(page.HasError.ToString()) is always true. Why?
page.HasError is of type bool. So, page.HasError.ToString() "Converts the value of this instance to its equivalent string representation (either "True" or "False")."
So, it will never be null or empty. So, string.IsNullOrEmpty will always be false, and !string.IsNullOrEmpty therefore always be true.
If you want to check the boolean value, you simply do if( page.HasError ) => "Page has an error"

How can I parse some JSON dynamically without knowing JSON values?

So I am using TDAmeritrade API to receive stock data with a C# Winforms program on Visual Studio. It takes the user input stock symbol and searches for the info. I am using HttpClient and Newtonsoft.Json and have been able to successfully perform the GET request and receive a JSON string back, but I do not know how to get all of the information I need out of it.
Here is the JSON:
https://drive.google.com/file/d/1TpAUwjyqrHArEXGXMof_K1eQe0hFoaw5/view?usp=sharing
Above is the JSON string sent back to me then formatted. My goal is to record information for each price in "callExpDateMap.2021-02-19:11" and "callExpDateMap.2021-03-19:39". The problem is that for each different stock, the dates that show up in "callExpDateMap" are going to be different.
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var response = await client.GetAsync(url);
var info = await response.Content.ReadAsStringAsync();
dynamic config = JsonConvert.DeserializeObject<dynamic>(info, new ExpandoObjectConverter());
return config;
This is the code I have right now. I know the last for statement is not correct. How can I parse to the specific sections I want (callExpDateMap.expirationdate.StrikePrice) and get the information needed from each without knowing the dates and Strike prices beforehand? Is there a way to innumerate it and search through the JSON as if it were all a bunch of arrays?
The code below is perhaps not the most elegant nor complete, but I think it will get you going. I would start by using the JObject.Parse() from the Newtonsoft.Json.Linq namespace and take it from there.
JObject root = JObject.Parse(info);
string symbol = root["symbol"].ToObject<string>();
foreach (JToken toplevel in root["callExpDateMap"].Children())
{
foreach (JToken nextlevel in toplevel.Children())
{
foreach (JToken bottomlevel in nextlevel.Children())
{
foreach (JToken jToken in bottomlevel.Children())
{
JArray jArray = jToken as JArray;
foreach (var arrayElement in jArray)
{
InfoObject infoObject = arrayElement.ToObject<InfoObject>();
Console.WriteLine(infoObject.putCall);
Console.WriteLine(infoObject.exchangeName);
Console.WriteLine(infoObject.multiplier);
}
}
}
}
}
public class InfoObject
{
public string putCall { get; set; }
public string symbol { get; set; }
public string description { get; set; }
public string exchangeName { get; set; }
// ...
public int multiplier { get; set; }
// ...
}
This is official documentation of Newtonsoft method you are trying to use.
https://www.newtonsoft.com/json/help/html/Overload_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
If an API's method returns different json propeties and you cannot trust it's property names all the times, then you can try using a deserialize method that returns .Net object, for example: JsonConvert.DeserializeObject Method (String)
https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject.htm
That method's signature is this:
public static Object DeserializeObject(string value)
Parameter is: value of type json string.
Return Value is: Object of type object.
If you do not want an Object, then you can of course use a .Net type you have. Such as this method:
JsonConvert.DeserializeObject Method (String)
Any property that you have in both (the .net type and json object) will get populated. If .net type has properties that do not exist in json object, then those will be ignored. If json object has properties that do not exist in.net, then those will be ignored too.
Here's an example of a .Net type
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace MyNameSpace
{
public class TDAmeritradeStockData
{
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("status")]
public string Status { get; set; }
[JsonProperty("callExpDateMap")]
public object CallExpDateMap { get; set; }
//...
//...
public CallExpDateMapType[] CallExpDateMapList { get; set; }
}
public class CallExpDateMapType
{
[JsonProperty("expirationdate")]
public string Expirationdate { get; set; }
[JsonProperty("StrikePrice")]
public List<StrikePriceType> StrikePriceList { get; set; }
}
public class StrikePriceType
{
public string StrikePrice { get; set; }
public List<StrikePricePropertiesType> StrikePricePropertiesList { get; set; }
}
public class StrikePricePropertiesType
{
[JsonProperty("putCall")]
public string PutCall { get; set; }
[JsonProperty("symbol")]
public string Symbol { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("exchangeName")]
public string ExchangeName { get; set; }
[JsonProperty("bid")]
public double Bid { get; set; }
[JsonProperty("ask")]
public double Ask { get; set; }
//...
//...
}
[TestClass]
public class TestTestTest
{
[TestMethod]
public void JsonTest()
{
var jsondata = ReadFile("data.json");
var model = JsonConvert.DeserializeObject<TDAmeritradeStockData>(jsondata);
JObject jObject = (JObject)model.CallExpDateMap;
var count = ((JObject)model.CallExpDateMap).Count;
model.CallExpDateMapList = new CallExpDateMapType[count];
var jToken = (JToken)jObject.First;
for (var i = 0; i < count; i++)
{
model.CallExpDateMapList[i] = new CallExpDateMapType
{
Expirationdate = jToken.Path,
StrikePriceList = new List<StrikePriceType>()
};
var nextStrikePrice = jToken.First.First;
while (nextStrikePrice != null)
{
var nextStrikePriceProperties = nextStrikePrice;
var srikePriceList = new StrikePriceType
{
StrikePrice = nextStrikePriceProperties.Path,
StrikePricePropertiesList = JsonConvert.DeserializeObject<List<StrikePricePropertiesType>>(nextStrikePrice.First.ToString())
};
model.CallExpDateMapList[i].StrikePriceList.Add(srikePriceList);
nextStrikePrice = nextStrikePrice.Next;
}
jToken = jToken.Next;
}
Assert.IsNotNull(model);
}
private string ReadFile(string fileName)
{
using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var data = new StringBuilder();
using (var streamReader = new StreamReader(fileStream))
{
while (!streamReader.EndOfStream) data.Append(streamReader.ReadLine());
streamReader.Close();
}
fileStream.Close();
return data.ToString();
}
}
}
}

JsonConvert.DeserializeObject() throwing System.ArgumentNullException: 'Path cannot be null. Parameter name: path' exception. Why?

My class structure is as follows:
class AppDetails
{
public String companyName { get; set; }
public String applicationName { get; set; }
public String version { get; set; }
public List<File_> fileObjectList { get; set; }
public AppDetails(String cName, String aName, String v)
{
companyName = cName;
applicationName = aName;
version = v;
}
}
class File_
{
public String filePath { get; set; }
public FileRecord fileRecord { get; set; }
public File_(String parent_, String filepath_, Boolean Ignored)
{
filePath = filepath_;
fileRecord = new FileRecord(parent_ + filePath, Ignored);
}
}
class FileRecord
{
public Boolean ignored { get; set; }
public String MD5Checksum { get; set; }
public int version { get; set; }
public FileRecord(String filePath, Boolean ignored_)
{
ignored = ignored_;
if (ignored)
{
MD5Checksum = null;
}
else
{
MD5Checksum = CalculateMD5(filePath);
version = 0;
}
}
static string CalculateMD5(string filePath)
{
var md5 = MD5.Create();
var stream = File.OpenRead(filePath);
var hash = md5.ComputeHash((System.IO.Stream)stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
}
I generated Json file using these classes. But when I try to initialize an object with the said JSON file, it throws System.ArgumentNullException: 'Path cannot be null. Parameter name: path' exception.
This is the function that is supposed to return the object.
AppDetails ReadJsonFile()
{
using (StreamReader r = File.OpenText(parent + "\\AllFilesList.json"))
{
string json = r.ReadToEnd();
var result = JsonConvert.DeserializeObject<AppDetails>(json);
return result;
}
}
I tried generating the classes from JSON using online class generators and it matches my class structure. Exception is thrown on this line:
var result = JsonConvert.DeserializeObject<AppDetails>(json);
Json string is getting loaded with the content from the file just fine(as can be seen from the watch). I couldn't find anything about Path issues related to JsonConvert.DeserializeObject. Most previous questions seems to be related to value errors.
EDIT: Serialization Code
void JsonWriter(AppDetails appDetails, String filename)
{
string path = parent + "\\" + filename + ".json";
File.Delete(path);
string json = JsonConvert.SerializeObject(appDetails);
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine(json.ToString());
tw.Close();
}
}
Sample Json File:
{"companyName":"Home","applicationName":"Test","version":"V5.0.1","fileObjectList":[{"filePath":"\\bug-tracker.exe","fileRecord":{"ignored":false,"MD5Checksum":"a5254a813a040b429f2288df737a8b9f","version":0}},{"filePath":"\\bug-tracker.exe.config","fileRecord":{"ignored":false,"MD5Checksum":"e5c3e9137dc8fadb57dfc27b0ba6855c","version":0}},{"filePath":"\\bug-tracker.pdb","fileRecord":{"ignored":false,"MD5Checksum":"9a9dfda29dcaacae82cba7bd7aa97ffa","version":0}},{"filePath":"\\Caliburn.Micro.dll","fileRecord":{"ignored":false,"MD5Checksum":"aa5f96c02b08d9b33322f3024058dd91","version":0}},{"filePath":"\\Caliburn.Micro.Platform.Core.dll","fileRecord":{"ignored":false,"MD5Checksum":"ab7867bd44b59879a59b5cb968e15668","version":0}},{"filePath":"\\Caliburn.Micro.Platform.Core.xml","fileRecord":{"ignored":false,"MD5Checksum":"cdfcbbf70a9a62b92e82a953ab9e7e30","version":0}},{"filePath":"\\Caliburn.Micro.Platform.dll","fileRecord":{"ignored":false,"MD5Checksum":"a52bdecbc1b7625cb13c9385fad4231b","version":0}},{"filePath":"\\Caliburn.Micro.Platform.xml","fileRecord":{"ignored":false,"MD5Checksum":"09f258a3aeca7285355d82a66dda2176","version":0}},{"filePath":"\\Caliburn.Micro.xml","fileRecord":{"ignored":false,"MD5Checksum":"c87ec582a4bfcf2e79e517c689441def","version":0}},{"filePath":"\\MaterialDesignColors.dll","fileRecord":{"ignored":false,"MD5Checksum":"ad729352a9088b889cc0c4dc7542dcb6","version":0}},{"filePath":"\\MaterialDesignColors.pdb","fileRecord":{"ignored":false,"MD5Checksum":"7ba70b23e22db9ac155e190860d9a5ec","version":0}},{"filePath":"\\MaterialDesignThemes.Wpf.dll","fileRecord":{"ignored":false,"MD5Checksum":"e4c790d3af41620dc5ad513ae7fcadac","version":0}},{"filePath":"\\MaterialDesignThemes.Wpf.pdb","fileRecord":{"ignored":false,"MD5Checksum":"f8113c8ea54896b8150db8e7ebd506ef","version":0}},{"filePath":"\\MaterialDesignThemes.Wpf.xml","fileRecord":{"ignored":false,"MD5Checksum":"49717f8130b7529ee51fb6bc13f79aa4","version":0}},{"filePath":"\\ShowMeTheXAML.dll","fileRecord":{"ignored":false,"MD5Checksum":"040b9e80820553a55f13ac19c2036367","version":0}},{"filePath":"\\System.Windows.Interactivity.dll","fileRecord":{"ignored":false,"MD5Checksum":"580244bc805220253a87196913eb3e5e","version":0}}]}
Edit 2: Json String from watch
"{\"companyName\":\"Home\",\"applicationName\":\"Test\",\"version\":\"V5.0.1\",\"fileObjectList\":[{\"filePath\":\"\\\\bug-tracker.exe\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"a5254a813a040b429f2288df737a8b9f\",\"version\":0}},{\"filePath\":\"\\\\bug-tracker.exe.config\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"e5c3e9137dc8fadb57dfc27b0ba6855c\",\"version\":0}},{\"filePath\":\"\\\\bug-tracker.pdb\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"9a9dfda29dcaacae82cba7bd7aa97ffa\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"aa5f96c02b08d9b33322f3024058dd91\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.Platform.Core.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"ab7867bd44b59879a59b5cb968e15668\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.Platform.Core.xml\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"cdfcbbf70a9a62b92e82a953ab9e7e30\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.Platform.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"a52bdecbc1b7625cb13c9385fad4231b\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.Platform.xml\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"09f258a3aeca7285355d82a66dda2176\",\"version\":0}},{\"filePath\":\"\\\\Caliburn.Micro.xml\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"c87ec582a4bfcf2e79e517c689441def\",\"version\":0}},{\"filePath\":\"\\\\MaterialDesignColors.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"ad729352a9088b889cc0c4dc7542dcb6\",\"version\":0}},{\"filePath\":\"\\\\MaterialDesignColors.pdb\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"7ba70b23e22db9ac155e190860d9a5ec\",\"version\":0}},{\"filePath\":\"\\\\MaterialDesignThemes.Wpf.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"e4c790d3af41620dc5ad513ae7fcadac\",\"version\":0}},{\"filePath\":\"\\\\MaterialDesignThemes.Wpf.pdb\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"f8113c8ea54896b8150db8e7ebd506ef\",\"version\":0}},{\"filePath\":\"\\\\MaterialDesignThemes.Wpf.xml\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"49717f8130b7529ee51fb6bc13f79aa4\",\"version\":0}},{\"filePath\":\"\\\\ShowMeTheXAML.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"040b9e80820553a55f13ac19c2036367\",\"version\":0}},{\"filePath\":\"\\\\System.Windows.Interactivity.dll\",\"fileRecord\":{\"ignored\":false,\"MD5Checksum\":\"580244bc805220253a87196913eb3e5e\",\"version\":0}}]}\r\n"
The actual issue is that you have the parameters in your constructor:
public FileRecord(String filePath, Boolean ignored_)
And JsonConvert puts there default values (null, false) which triggers the code:
else
{
MD5Checksum = CalculateMD5(filePath);
version = 0;
}
Which in its turn tries to read from a file using null path parameter:
static string CalculateMD5(string filePath)
{
var md5 = MD5.Create();
var stream = File.OpenRead(filePath); // <- HERE!!!!
var hash = md5.ComputeHash((System.IO.Stream)stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
I see there two solutions
Create parameterless constructors where init everything as you need or call the constructor with parameters using :this() with defaults:
public FileRecord() : this(null, true)
{
}
Rename properties to match parameter names, like:
public bool Ignored { get; set; }
public FileRecord(string filePath, bool ignored)
{
this.Ingnoerd = ignored;
....
}

Parse JSON String into List

It is needed to parse JSONString into List. (List of instances)
I'm trying to use JSON.NET by Newtonsoft.
I have classes:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public string Manufactorers { get; set; }
}
The JSON string looks something like this:
[
{
"Column0":23.0,
"Column1":"Евроен",
"Column2":"https://www.123.com",
"Column3":"Фак"
},
{
"Column0":24.0,
"Column1":"Еил",
"Column2":"https://www.123.com",
"Column3":"Старт"
}
]
I've been trying to do something like this:
string JSONString = string.Empty;
JSONString = JsonConvert.SerializeObject(result);
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(JSONString);
But it returns 0 and null.
I have no idea, how to fix it.
Also here I truy to parse Excel file. This code works, but after deserialization, I have just 0 and null.
var filePath = #"..\..\..\..\doc.xlsx";
using (var steam = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = ExcelReaderFactory.CreateReader(steam))
{
var result = reader.AsDataSet().Tables["Лист1"];
string JSONString = string.Empty;
JSONString = JsonConvert.SerializeObject(result);
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(JSONString);
}
}
The naming of JSON and your class does not match. This can be fixed using JsonProperty attributes:
[JsonProperty("Column0")]
public decimal ID { get; set; }
Second, JSON deserizlizer can not deserialize string "23.0" to int when there is decimal point. You can retype ID to decimal or double to make it work.
Little test here:
public class TestClass
{
[JsonProperty("Column0")]
public decimal ID { get; set; }
}
Then the deserialization works without errors:
var testClassJson = "{\"Column0\": 12.0}";
var i = JsonConvert.DeserializeObject<TestClass>(testClassJson);

Composite join on two CSV files in C#

Starting from a table of daily fruit prices
fruits.csv
Day,Name,Kind,Price
2019-09-04,"apple","red",63.09
2019-09-04,"apple","yellow",52.14
2019-09-04,"orange","navel",41.18
2019-09-04,"orange","blood",41.18
2019-09-03,"apple","red",63.07
2019-09-03,"apple","yellow",52.11
2019-09-03,"orange","navel",41.13
2019-09-03,"orange","blood",41.13
I'd like to insert the reference prices by name and kind
fruit_ref_prices.csv
Name,Kind,Reference_Price
"apple","red",60.00
"apple","yellow",50.00
"orange","navel",40.00
"orange","blood",42.00
to result in the following table
Day,Name,Kind,Price,Reference_Price
2019-09-04,"apple","red",63.09,60.00
2019-09-04,"apple","yellow",52.14,50.00
2019-09-04,"orange","navel",41.18,40.00
2019-09-04,"orange","blood",41.18,42.00
2019-09-03,"apple","red",63.07,60.00
2019-09-03,"apple","yellow",52.11,50.00
2019-09-03,"orange","navel",41.13,40.00
2019-09-03,"orange","blood",41.13,42.00
The solution should be simple using C#'s built-in SQL-like syntax, and I'm sure the answer lies in one of the following tutorial pages:
Join clause
Perform custom join operations
Join by using composite keys
but I'm having a hard time identifying the syntax of this language.
In my attempt below instead of writing
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
I should be able to write
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
and fruit.kind equals fruit_ref.kind
but the Boolean expression is not accepted. Why?
My attempt is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
namespace MyConsoleApplication
{
class Program
{
const string root = #"c:\path\to\here\";
const string file1_in = root + #"fruits.csv";
const string file2_in = root + #"fruit_ref_prices.csv";
static void Main(string[] args)
{
Fruit_Basket fruit_basket = new Fruit_Basket(file1_in, file2_in);
fruit_basket.PrintFruits();
}
}
public class Fruit
{
public DateTime day { get; set; }
public string name { get; set; }
public string kind { get; set; }
public decimal price { get; set; }
public Fruit(DateTime newFruit_day,
string newFruit_name,
string newFruit_kind,
decimal newFruit_price)
{
this.day = newFruit_day;
this.name = newFruit_name;
this.kind = newFruit_kind;
this.price = newFruit_price;
}
}
public class Fruit_Ref
{
public string name;
public string kind;
public decimal reference_price;
public Fruit_Ref(string newName, string newKind, decimal newRef_Price)
{
this.name = newName;
this.kind = newKind;
this.reference_price = newRef_Price;
}
}
public class Fruit_Basket {
public List<Fruit> fruits { get; set; }
public List<Fruit_Ref> fruit_refs { get; set; }
public Fruit_Basket(string file1_in, string file2_in) {
build_fruit_list(file1_in);
build_fruit_ref_list(file2_in);
}
public void build_fruit_list(string file_in)
{
fruits = new List<Fruit>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_day = DateTime.Parse(splitLine[0]);
var newFruit_name = splitLine[1];
var newFruit_kind = splitLine[2];
var newFruit_price = decimal.Parse(splitLine[3]);
Fruit newFruit = new Fruit(newFruit_day,
newFruit_name,
newFruit_kind,
newFruit_price);
fruits.Add(newFruit);
}
}
reader.Close();
}
public void build_fruit_ref_list(string file_in)
{
fruit_refs = new List<Fruit_Ref>();
int count = 0;
StreamReader reader = new StreamReader(file_in);
string line = "";
while ((line = reader.ReadLine()) != null)
{
if (++count > 1)
{
string[] splitLine = line.Split(new char[] { ',' }).ToArray();
var newFruit_name = splitLine[0];
var newFruit_kind = splitLine[1];
var newFruit_ref_price = decimal.Parse(splitLine[2]);
Fruit_Ref newFruit_ref = new Fruit_Ref(newFruit_name,
newFruit_kind,
newFruit_ref_price);
fruit_refs.Add(newFruit_ref);
}
}
reader.Close();
}
public void PrintFruits()
{
var innerJoinQuery =
from fruit in fruits
join fruit_ref in fruit_refs on fruit.name equals fruit_ref.name
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };
Console.WriteLine($#"""Date"",""Name"",""Kind"",""Price"",""Ref Price""");
foreach (var i in innerJoinQuery)
{
Console.WriteLine($#"{i.Day},{i.Kind},{i.Price},{i.Reference_Price}");
}
}
}
}
You could also refactor your code to use the CsvHelper NuGet package for reading/writing CSV files.
First, You can make these classes to reflect the fruits, fruit references and final fruit structure.
public class Fruit
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
}
public class FruitReferencePrice
{
public string Name { get; set; }
public string Kind { get; set; }
public string Reference_Price { get; set; }
}
public class FruitFinal
{
public string Day { get; set; }
public string Name { get; set; }
public string Kind { get; set; }
public string Price { get; set; }
public string ReferencePrice { get; set; }
public override string ToString()
{
return $"Day={Day},Name={Name},Kind={Kind},Price={Price},Reference_Price={ReferencePrice}";
}
}
Then you can make two methods to return the rows of each CSV file into List<Fruit> and List<FruitReferencePrice>.
private static IEnumerable<Fruit> BuildFruitList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<Fruit>().ToList();
} catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<Fruit>();
}
}
private static IEnumerable<FruitReferencePrice> BuildFruitReferenceList(string csvFilePath)
{
if (!File.Exists(csvFilePath))
{
throw new FileNotFoundException("Could not locate CSV at path " + csvFilePath, csvFilePath);
}
try
{
using var fileReader = File.OpenText(csvFilePath);
using var csv = new CsvReader(fileReader);
return csv.GetRecords<FruitReferencePrice>().ToList();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return Enumerable.Empty<FruitReferencePrice>();
}
}
Then you can perform a grouped join and output the merged result.
var path1 = "PATH\\fruits.csv";
var path2 = "PATH\\fruit_ref_prices.csv";
var fruitList = BuildFruitList(path1);
var fruitReferencePrices = BuildFruitReferenceList(path2);
var groupedJoin = from fruit in fruitList
join fruit_ref in fruitReferencePrices
on new { fruit.Name, fruit.Kind } equals new { fruit_ref.Name, fruit_ref.Kind }
select new FruitFinal
{
Day = fruit.Day,
Name = fruit.Name,
Kind = fruit.Kind,
Price = fruit.Price,
ReferencePrice = fruit_ref.Reference_Price
};
foreach (var fruit in groupedJoin)
{
Console.WriteLine(fruit.ToString());
}
Merged results:
Day=2019-09-04,Name=apple,Kind=red,Price=63.09,Reference_Price=60.00
Day=2019-09-04,Name=apple,Kind=yellow,Price=52.14,Reference_Price=50.00
Day=2019-09-04,Name=orange,Kind=navel,Price=41.18,Reference_Price=40.00
Day=2019-09-04,Name=orange,Kind=blood,Price=41.18,Reference_Price=42.00
Day=2019-09-03,Name=apple,Kind=red,Price=63.07,Reference_Price=60.00
Day=2019-09-03,Name=apple,Kind=yellow,Price=52.11,Reference_Price=50.00
Day=2019-09-03,Name=orange,Kind=navel,Price=41.13,Reference_Price=40.00
Day=2019-09-03,Name=orange,Kind=blood,Price=41.13,Reference_Price=42.00
Please change the equals clause as on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
Why you require this
The query has two anonymous types (one for left table and one for right table). So to compare those anonymous types, the linq statement should use new keyword
Query :
var innerJoinQuery = from fruit in fruits
join fruit_ref in fruit_refs on new { fruit.name, fruit.kind } equals new { fruit_ref.name, fruit_ref.kind }
select new { Day = fruit.day, Name = fruit.name, Kind = fruit.kind,
Price = fruit.price, Reference_Price = fruit_ref.reference_price };

Categories