Parse/Extract information from text template - c#

i need to extract information from incoming (e.g. xml) data based on a given template.
The template may be XML or plain text (comma separated). For each type of message there exists a template, e.g.
<SomeMessage>
<Id>$id</Id>
<Position>
<X>$posX</X>
<Y>$posY</Y>
<Z>$posZ</Z>
</Position>
</SomeMessage>
The incoming data for example is:
<SomeMessage>
<Id>1</Id>
<Position>
<X>0.5f</X>
<Y>1.0f</Y>
<Z>0.0f</Z>
</Position>
</SomeMessage>
Now i need to extract information about $id, $posX, etc.
Parser p = new Parser(templateString);
int id = p.Extract("id", incomingString);
float posx = p.Extract("posX", incomingString);
I need something like the difference of incomingData and template and then extract the information at the appropiate position. Because there exist several tempaltes which contain different information and may be extended in the future i am looking for a general approach.
The template in this case may also be
$id,$posX,$posY,$posZ
and the incoming data would be then
1,0.5f,1.0f,0.0f
The latter case may be eaiser to parse, but i need a solution which is able the handle both (xml template as well as non xml).

You could create a parsing class having a property for each field:
class Parser
{
public string PositionX { get; set; }
public string PositionY { get; set; }
public string PositionZ { get; set; }
public Parser(XmlNode item)
{
this.PositionX = GetNodeValue(item, "Position/X");
this.PositionY = GetNodeValue(item, "Position/X/Y");
this.PositionZ = GetNodeValue(item, "Position/X/Y/Z");
}
}
I can supply a routine that can generate such parsing classes from sample xml if your interested, when arrays do not concern. GetNodeValue is a method that uses an xpath query and returns the value for the xpath (basicly XmlNode.SelectSingleNode with some added parsing added to it).

It would probably be a good idea to use a Interface and 2 different templates for each occasion. Note that the returned Message is not completed but it gives you an idea. With the static XElement.Parse you can parse well formed XML strings for easier usage.
public interface IParser
{
Message Parse(String Payload);
}
// Position Class
public class Position
{
public int X { get; private set; }
public int Y { get; private set; }
public int Z { get; private set; }
public Position(int X, int Y, int Z)
{
this.X = X;
this.Y = Y;
this.Z = Z;
}
}
// Message Class
public class Message
{
public String ID { get; private set; }
public Position Position { get; private set; }
public Message(String ID, Position Position)
{
this.ID = ID;
this.Position = Position;
}
}
// Parser Class
public class XMLParser : IParser
{
public Message Parse(string Payload)
{
var result = XElement.Parse(Payload);
return new Message(result.Elements().ElementAt(0).Value, new Position(X,Y,Z);
}
}

For each template create a parser definition file of the format:
Parser Type (XML or CSV)
Variable1, path
variable2, path
etc
for xml path might be someMessage,Position,x.
for csv you might forget the path and just list the variables in order.
Then when you read in your template file your pick up the parser type and the path to each of the variables. If you have a lot of hierarchical information then you'll have to apply a bit of imagination to this but it should be fine for the simple case you've given.
For anything over CSV you will probably have to use a parser, but XML/XPATH is pretty simple to find the basics for.

using System;
using System.IO;
using System.Xml;
class TemplateParse {
XmlDocument xdoc;
string GetPath(XmlNode node, string val, string path){
if(node.HasChildNodes){
if(node.ChildNodes.Count == 1 && node.FirstChild.NodeType == XmlNodeType.Text)
return (node.FirstChild.Value == val) ? path + "/" + node.Name : String.Empty;
foreach(XmlNode cnode in node.ChildNodes){
if(cnode.NodeType != XmlNodeType.Element) continue;
string result = GetPath(cnode, val, path + "/" + node.Name);
if(result != String.Empty) return result;
}
}
return "";
}
public TemplateParse(string templateXml){
xdoc = new XmlDocument();
xdoc.LoadXml(templateXml);
}
public string Extract(string valName, string data){
string xpath = GetPath((XmlNode)xdoc.DocumentElement, "$" + valName, "/");
var doc = new XmlDocument();
doc.LoadXml(data);
return doc.SelectSingleNode(xpath).InnerText;
// var value = doc.SelectSingleNode(xpath).InnerText;
// var retType = typeof(T);
// return (T)retType.InvokeMember("Parse", System.Reflection.BindingFlags.InvokeMethod, null, null, new []{value});
}
}
class Sample {
static void Main(){
string templateString = File.ReadAllText(#".\template.xml");
string incomingString = File.ReadAllText(#".\data.xml");
var p = new TemplateParse(templateString);
string[] names = new [] { "id", "posX", "posY", "posZ" };
foreach(var name in names){
var value = p.Extract(name, incomingString);
Console.WriteLine("{0}:{1}", name, value);
}
}
}
OUTPUT
id:1
posX:0.5f
posY:1.0f
posZ:0.0f

Related

Read nodes of a xml file in C#

How can I read the following xml file into a List:
Partial XML file (data.log)
<ApplicationLogEventObject>
<EventType>Message</EventType>
<DateStamp>10/13/2016 11:15:00 AM</DateStamp>
<ShortDescription>N/A</ShortDescription>
<LongDescription>Sending 'required orders' email.</LongDescription>
</ApplicationLogEventObject>
<ApplicationLogEventObject>
<EventType>Message</EventType>
<DateStamp>10/13/2016 11:15:10 AM</DateStamp>
<ShortDescription>N/A</ShortDescription>
<LongDescription>Branches Not Placed Orders - 1018</LongDescription>
</ApplicationLogEventObject>
<ApplicationLogEventObject>
<EventType>Message</EventType>
<DateStamp>10/13/2016 11:15:10 AM</DateStamp>
<ShortDescription>N/A</ShortDescription>
<LongDescription>Branches Not Placed Orders - 1019</LongDescription>
</ApplicationLogEventObject>
...
And here is the data access layer (DAL):
public List<FLM.DataTypes.ApplicationLogEventObject> Get()
{
try
{
XmlTextReader xmlTextReader = new XmlTextReader(#"C:\data.log");
List<FLM.DataTypes.ApplicationLogEventObject> recordSet = new List<ApplicationLogEventObject>();
xmlTextReader.Read();
while (xmlTextReader.Read())
{
xmlTextReader.MoveToElement();
FLM.DataTypes.ApplicationLogEventObject record = new ApplicationLogEventObject();
record.EventType = xmlTextReader.GetAttribute("EventType").ToString();
record.DateStamp = Convert.ToDateTime(xmlTextReader.GetAttribute("DateStamp"));
record.ShortDescription = xmlTextReader.GetAttribute("ShortDescription").ToString()
record.LongDescription = xmlTextReader.GetAttribute("LongDescription").ToString();
recordSet.Add(record);
}
return recordSet;
}
catch (Exception ex)
{
throw ex;
}
}
And the Data Types which will hold the child elements from the XML file:
public class ApplicationLogEventObject
{
public string EventType { get; set; }
public DateTime DateStamp { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
}
After I've read the child nodes into a List I would then like to return it and display it in a DataGridView.
Any help regarding this question will be much appreciated.
Your log file is not an XML document. Since an XML document must have one and only one root element, it's a series of XML documents concatenated together. Such a series of documents can be read by XmlReader by setting XmlReaderSettings.ConformanceLevel == ConformanceLevel.Fragment. Having done so, you can read through the file and deserialize each root element individually using XmlSerializer as follows:
static List<ApplicationLogEventObject> ReadEvents(string fileName)
{
return ReadObjects<ApplicationLogEventObject>(fileName);
}
static List<T> ReadObjects<T>(string fileName)
{
var list = new List<T>();
var serializer = new XmlSerializer(typeof(T));
var settings = new XmlReaderSettings { ConformanceLevel = ConformanceLevel.Fragment };
using (var textReader = new StreamReader(fileName))
using (var xmlTextReader = XmlReader.Create(textReader, settings))
{
while (xmlTextReader.Read())
{ // Skip whitespace
if (xmlTextReader.NodeType == XmlNodeType.Element)
{
using (var subReader = xmlTextReader.ReadSubtree())
{
var logEvent = (T)serializer.Deserialize(subReader);
list.Add(logEvent);
}
}
}
}
return list;
}
Using the following version of ApplicationLogEventObject:
public class ApplicationLogEventObject
{
public string EventType { get; set; }
[XmlElement("DateStamp")]
public string DateStampString {
get
{
// Replace with culturally invariant desired formatting.
return DateStamp.ToString(CultureInfo.InvariantCulture);
}
set
{
DateStamp = Convert.ToDateTime(value, CultureInfo.InvariantCulture);
}
}
[XmlIgnore]
public DateTime DateStamp { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
}
Sample .Net fiddle.
Notes:
The <DateStamp> element values 10/13/2016 11:15:00 AM are not in the correct format for dates and times in XML, which is ISO 8601. Thus I introduced a surrogate string DateStampString property to manually handle the conversion from and to your desired format, and then marked the original DateTime property with XmlIgnore.
Using ReadSubtree() prevents the possibility of reading past the end of each root element when the XML is not indented.
According to the documentation for XmlTextReader:
Starting with the .NET Framework 2.0, we recommend that you use the System.Xml.XmlReader class instead.
Thus I recommend replacing use of that type with XmlReader.
The child nodes of your <ApplicationLogEventObject> are elements not attributes, so XmlReader.GetAttribute() was not an appropriate method to use to read them.
Given that your log files are not formatting their times in ISO 8601, you should at least make sure they are formatted in a culturally invariant format so that log files can be exchanged between computers with different regional settings. Doing your conversions using CultureInfo.InvariantCulture ensures this.

Serialization: Dynamic class names

I have already tried various possibilities but maybe I am just too tired of seeing the solution -.-
I have an xml structure like this:
<diagnosisList>
<diagnosis>
<surgery1>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery1>
<surgery2>
<date>1957-08-13</date>
<description>a</description>
<ops301>0-000</ops301>
</surgery2>
<surgery...>
</surgery...>
</diagnosis>
</diagnosisList>
As you see there is a variable number of surgeries. I have a class "surgery" containing the XML elements.
class Surgery
{
[XmlElement("date")]
public string date { get; set; }
[XmlElement("description")]
public string description { get; set; }
[XmlElement("ops301")]
public string ops301 { get; set; }
public Surgery()
{
}
}
and a class diagnosis creating the structure by adding the surgery class to the constructor.
diagnosis.cs
class Diagnosis
{
[XmlElement("surgery")]
public Surgery surgery
{
get;
set;
}
public Diagnosis(Surgery Surgery)
{
surgery = Surgery;
}
}
I need to be able to serialize the class name of the surgery dynamically by adding a number before serialization happens.
does anybody know a way to achieve that?
any help is really appreciated :)
Kind regards
Sandro
-- EDIT
I create the whole structure starting from my root class "Import". this class then will be passed to the serializer. So I cannot use XMLWriter in the middle of creation of the structure. I Need to create the whole structure first and finally it will be serialized:
private static void XmlFileSerialization(Import import)
{
string filename = #"c:\dump\trauma.xml";
// default file serialization
XmlSerializer<Import>.SerializeToFile(import, filename);
XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
namespaces.Add("", "");
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.IndentChars = "\t";
XmlSerializer<Import>.SerializeToFile(import, filename, namespaces, settings);
}
and then in the Method "SerializeToFile"
public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces, XmlWriterSettings settings)
{
if (source == null)
throw new ArgumentNullException("source", "Object to serialize cannot be null");
XmlSerializer serializer = new XmlSerializer(source.GetType());
using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
x.Serialize(xmlWriter, source, namespaces);
}
}
}
What I Need is to be able to instantiate a variable number of classes based on the main class "Surgery". The class must have a variable Name, i.e.
surgery1, surgery2, surgery3, etc.
This cannot be changed because this is given by the Institution defining the XML structure.
the class must be accessible by its dynamic Name because the property in the class must be set.
so:
surgery1.Property = "blabla";
surgery2. Property = "babla";
etc.
I am even thinking about using T4 methods to create this part of code, but there must be another way to achieve dynamic class names.
I also thought of creating instances with variable names of the class by using reflection:
System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(string className)
But this doesn't work actually -.-
Does anybody have a hint and could put me in the right direction?
I think, you could try implement methods from IXmlSerializable in object contains diagnosisList.
Try to use custom xml writer and reader.
public class SurgeryWriter : XmlTextWriter
{
public SurgeryWriter(string url) : base(url, Encoding.UTF8) { }
private int counter = 1;
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (localName == "surgery")
{
base.WriteStartElement(prefix, "surgery" + counter, ns);
counter++;
}
else
base.WriteStartElement(prefix, localName, ns);
}
}
public class SurgeryReader : XmlTextReader
{
public SurgeryReader(string url) : base(url) { }
public override string LocalName
{
get
{
if (base.LocalName.StartsWith("surgery"))
return "surgery";
return base.LocalName;
}
}
}
Classes:
[XmlRoot("diagnosisList")]
public class DiagnosisList
{
[XmlArray("diagnosis")]
[XmlArrayItem("surgery")]
public Surgery[] Diagnosis { get; set; }
}
[XmlRoot("surgery")]
public class Surgery
{
[XmlElement("date", DataType = "date")]
public DateTime Date { get; set; }
[XmlElement("description")]
public string Description { get; set; }
[XmlElement("ops301")]
public string Ops301 { get; set; }
}
Use:
var xs = new XmlSerializer(typeof(DiagnosisList));
DiagnosisList diagnosisList;
using (var reader = new SurgeryReader("test.xml"))
diagnosisList = (DiagnosisList)xs.Deserialize(reader);
using (var writer = new SurgeryWriter("test2.xml"))
xs.Serialize(writer, diagnosisList);
Don't mix XML and C#.
You don't need dynamic names in the C# code!
If you need an arbitrary number of instances of a class, create them in a loop and place it in any collection.
var surgeries = new List<Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeries.Add(surgery);
}
Later you can access them by index or by enumerating.
surgeries[5]
foreach (var surgery in surgeries)
{
// use surgery
}
As you can see no need dynamic names!
Alternatively, use the dictionary with arbitrary names as keys.
var surgeryDict = new Dictionary<string, Surgery>();
for (int i = 0; i < 10; i++)
{
var surgery = new Surgery();
surgeryDict["surgery" + i] = surgery;
}
Access by name:
surgeryDict["surgery5"]

C# Writing to XML file does not save object

I have some experience in Java. Today I started programming in C# because I like Visual Studio.
As an exercise i'm building a system that manages all employees in a company.
Im working on a class called Function:
public class Function
{
private String functionname;
private String department;
private double hourpay;
public String getFunc()
{
return functionname;
}
public String getDepartement()
{
return department;
}
public double getPay()
{
return hourpay;
}
public String toString()
{
return ("Function: " + functionname + "\n" + "Department: " + department + "\n" + "Hourly Pay: " + hourpay);
}
public void setFunctionName(string functionname)
{
this.functionname = functionname;
}
public void setDepartment(String department)
{
this.department = department;
}
public void setPay(double pay)
{
this.hourpay = pay;
}
A very simple basic class to model a function, Now I would like to save functions in a XML file.
What I have in mind is:
After I created a function I put it in a list called Functions (List functions)
I write the list to an XML file.
If I want to update the XML file I simply load the List, add a new function, and then overwrite it to the XML file.
I made my class functions like this;
public class Functions{
public List<Function> functions = new List<Function>();
public void addFunction(Function func){
functions.Add(func);
}
public void writeFunctions()
{
System.Xml.Serialization.XmlSerializer writer =
new System.Xml.Serialization.XmlSerializer(typeof(Functions));
System.IO.StreamWriter file = new System.IO.StreamWriter(
#"C:\CDatabase\Functions.xml");
writer.Serialize(file, this);
file.Close();
}
}
To test it I implemented this in a click event of a button:
Function programmer = new Function();
schoonmaker.setFunctionName("Programmer");
schoonmaker.setDepartment("IT");
schoonmaker.setPay(16.50);
FunctionDatabase.addFunction(schoonmaker);
FunctionDatabase.writeFunctions();
It creates the XML file(If its not there), and this is in the XML file:
<Functions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<functions>
<Function />
</functions>
</Functions>
Its empty, and I dont understand why and how to fix it.
I think i'm thinking too easy, but I cant find a fix.
Thanks in advance,
Chris
C# doesn't use getter and setter methods like Java. Create your properties like this:
private string functionname;
public string functionname
{
get { return functionname; }
set { functionname = value; }
}
A suggestion would be to use properties instead of getter/setter functions - they are a feature of C# which are used a lot.
For example, instead of:
public double getPay()
{
return hourpay;
}
Try:
public double HourPay { get; set; }
Or if you want to be able to perform actions within the getter/setter:
private double hourPay;
public double HourPay
{
get
{
return hourPay;
}
set
{
hourPay = value;
}
}
By using public properties in this way, the XML serializer should generate the file you were expecting.

XmlSerializer; System.invalidOperationException

EDIT: Using the code provided by Jon Skeet.
I get the following error:
Message: There is an error in XML document (2, 2).
Inner Exception: {"<Translator xmlns=''> was not expected."}
If it helps I can provide the code below:
Translator.cs:
public class Translator
{
public FullBotTranslation Translation;
public Translator()
{
Translation = new FullBotTranslation();
}
public void LoadLanguage(string language)
{
if (!Useful.ExistFile(System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + language + ".xml"))
language = "Francais";
Translation = XmlSerializerHelper.Deserialize<FullBotTranslation>(System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + language + ".xml");
}
public string GetTranslation(PhraseID phraseId)
{
foreach (Phrase phrase in Translation.Phrases)
{
if (phrase.PhraseID == phraseId)
return phrase.PhraseString;
}
return "Incomplete translation...";
}
#region Nested type: Translation
[Serializable]
public class FullBotTranslation
{
public List<Phrase> Phrases = new List<Phrase>();
}
#endregion
}
Phrase.cs:
public class Phrase
{
public PhraseID PhraseID { set; get; }
public string PhraseString{ set; get; }
public Phrase()
{
}
}
PhraseID.cs
[Serializable]
public enum PhraseID
{
none,
Button_Start,
Button_Stop,
}
I use it like this:
Setup:
private Translator _translator;
_translator = new Translator();
Saving:
Helpers.XmlSerializerHelper.Serialize(
System.AppDomain.CurrentDomain.BaseDirectory + "\\LanguagePacks\\" + langPackName.Text + ".xml",
_translator);
Loading:
_translator = new Translator(); //yes this is needed ;)
_translator.LoadLanguage(preloadedLangCombo.SelectedItem.ToString());
When using the code above to save an XML file it outputs the following:
English.XML:
<?xml version="1.0"?>
<Translator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Translation>
<Phrases>
<Phrase>
<PhraseID>none</PhraseID>
<PhraseString>Incomplete Translation</PhraseString>
</Phrase>
<Phrase>
<PhraseID>Button_Start</PhraseID>
<PhraseString>Start</PhraseString>
</Phrase>
<Phrase>
<PhraseID>Button_Stop</PhraseID>
<PhraseString>Stop</PhraseString>
</Phrase>
</Phrases>
</Translation>
</Translator>
It's not clear what's going wrong here - particularly because the error message doesn't seem to match your sample XML. Your exception handling may well be hiding problems though - it's a really bad idea to catch all exceptions like that, and you're going to unnecessary lengths to close the streams involved. I would condense your class down to just this:
public static class XmlSerializerHelper
{
public static void Serialize(String path, object #object)
{
using (var stream = File.Create(path))
{
var s = new XmlSerializer(#object.GetType());
s.Serialize(stream, #object);
}
}
public static T Deserialize<T>(String path)
{
using (var stream = File.OpenRead(path))
{
var s = new XmlSerializer(typeof(T));
return (T) s.Deserialize(stream);
}
}
}
Now if something goes wrong, the exception will propagate out of the methods, rather than being disguised. Also, note that I've serialized to/from just streams, rather than getting a StreamWriter involved. It's simpler to let the XML infrastructure deal with all the encoding.
The above code works fine for me in a simple test.

Read and parse a Json File in C#

How does one read a very large JSON file into an array in c# to be split up for later processing?
I have managed to get something working that will:
Read the file Miss out headers and only read values into array.
Place a certain amount of values on each line of an array. (So I
could later split it an put into 2d array)
This was done with the code below but it crashes the program after entering a few lines into the array. This might have to do with the file size.
// If the file extension was a jave file the following
// load method will be use else it will move on to the
// next else if statement
if (fileExtension == ".json")
{
int count = 0;
int count2 = 0;
int inOrOut = 0;
int nRecords=1;
JsonTextReader reader = new JsonTextReader(new StreamReader(txtLoaction.Text));
string[] rawData = new string[5];
while (reader.Read())
{
if (reader.Value != null)
if (inOrOut == 1)
{
if (count == 6)
{
nRecords++;
Array.Resize(ref rawData, nRecords);
//textBox1.Text += "\r\n";
count = 0;
}
rawData[count2] += reader.Value + ","; //+"\r\n"
inOrOut = 0;
count++;
if (count2 == 500)
{
MessageBox.Show(rawData[499]);
}
}
else
{
inOrOut = 1;
}
}
}
A snippet of the JSON I am working with is:
[
{ "millis": "1000",
"stamp": "1273010254",
"datetime": "2010/5/4 21:57:34",
"light": "333",
"temp": "78.32",
"vcc": "3.54" },
]
I need the values out of this JSON. For example, I need "3.54", but I would not want it to print the "vcc".
How can one read a JSON file in and only extract the data needed to be put into an array?
How about making everything easier with Json.NET?
public void LoadJson()
{
using (StreamReader r = new StreamReader("file.json"))
{
string json = r.ReadToEnd();
List<Item> items = JsonConvert.DeserializeObject<List<Item>>(json);
}
}
public class Item
{
public int millis;
public string stamp;
public DateTime datetime;
public string light;
public float temp;
public float vcc;
}
You can even get the values dynamically without declaring Item class.
dynamic array = JsonConvert.DeserializeObject(json);
foreach(var item in array)
{
Console.WriteLine("{0} {1}", item.temp, item.vcc);
}
Doing this yourself is an awful idea. Use Json.NET. It has already solved the problem better than most programmers could if they were given months on end to work on it. As for your specific needs, parsing into arrays and such, check the documentation, particularly on JsonTextReader. Basically, Json.NET handles JSON arrays natively and will parse them into strings, ints, or whatever the type happens to be without prompting from you. Here is a direct link to the basic code usages for both the reader and the writer, so you can have that open in a spare window while you're learning to work with this.
This is for the best: Be lazy this time and use a library so you solve this common problem forever.
Answer for .NET Core
You can just use the built-in System.Text.Json instead of the 3rd-party Json.NET. To promote reuse, the JSON-file-reading functionality belongs in its own class and should be generic rather than hard-coded to a certain type (Item). Here's a full example:
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
namespace Project
{
class Program
{
static async Task Main()
{
Item item = await JsonFileReader.ReadAsync<Item>(#"C:\myFile.json");
}
}
public static class JsonFileReader
{
public static async Task<T> ReadAsync<T>(string filePath)
{
using FileStream stream = File.OpenRead(filePath);
return await JsonSerializer.DeserializeAsync<T>(stream);
}
}
public class Item
{
public int millis;
public string stamp;
public DateTime datetime;
public string light;
public float temp;
public float vcc;
}
}
Or, if you prefer something simpler/synchronous:
class Program
{
static void Main()
{
Item item = JsonFileReader.Read<Item>(#"C:\myFile.json");
}
}
public static class JsonFileReader
{
public static T Read<T>(string filePath)
{
string text = File.ReadAllText(filePath);
return JsonSerializer.Deserialize<T>(text);
}
}
This can also be done in the following way:
JObject data = JObject.Parse(File.ReadAllText(MyFilePath));
string jsonFilePath = #"C:\MyFolder\myFile.json";
string json = File.ReadAllText(jsonFilePath);
Dictionary<string, object> json_Dictionary = (new JavaScriptSerializer()).Deserialize<Dictionary<string, object>>(json);
foreach (var item in json_Dictionary)
{
// parse here
}
Based on #L.B.'s solution, the (typed as Object rather than Anonymous) VB code is
Dim oJson As Object = JsonConvert.DeserializeObject(File.ReadAllText(MyFilePath))
I should mention that this is quick and useful for constructing HTTP call content where the type isn't required. And using Object rather than Anonymous means you can maintain Option Strict On in your Visual Studio environment - I hate turning that off.
For any of the JSON parse, use the website http://json2csharp.com/ (easiest way) to convert your JSON into C# class to deserialize your JSON into C# object.
public class JSONClass
{
public string name { get; set; }
public string url { get; set; }
public bool visibility { get; set; }
public string idField { get; set; }
public bool defaultEvents { get; set; }
public string type { get; set; }
}
Then use the JavaScriptSerializer (from System.Web.Script.Serialization), in case you don't want any third party DLL like newtonsoft.
using (StreamReader r = new StreamReader("jsonfile.json"))
{
string json = r.ReadToEnd();
JavaScriptSerializer jss = new JavaScriptSerializer();
var Items = jss.Deserialize<JSONClass>(json);
}
Then you can get your object with Items.name or Items.Url etc.
Very Easiest way I found on online to work with .JSON file in C#(or any other Programming Language)
Prerequisite:-
Install Newtonsoft.Json Library into your Project
Newtonsoft.Json
and here is the URL -> https://app.quicktype.io/
Steps
1> go to this URL - https://app.quicktype.io/
2> Copy and Paste your JSON file structure into Left sidebar
app.quicktype.io
3> Select required Language (here C#) from Options menu
4> Copy generated code and go to your Project and Create a new .cs file with the same name(here "Welcome.cs")
Welcome.cs
5> Paste all generated code into the newly created class.
Welcome.cs pasted Code
6> that's it. :)
Steps to Access value
1> Go to Main Program .cs file or wherever you need to access it.
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Access Json values using Keys.>");
String jsonString = new StreamReader("give <.json> file Path here").ReadToEnd();
// use below syntax to access JSON file
var jsonFile = Welcome.FromJson(jsonString);
string FileName = jsonFile.File;
long Lvl = jsonFile.Level;
bool isTrue = jsonFile.CSharp;
Console.WriteLine(FileName);//JSON
Console.WriteLine(Lvl);//1
Console.WriteLine(isTrue);//true
}
}
For finding the right path I'm using
var pathToJson = Path.Combine("my","path","config","default.Business.Area.json");
var r = new StreamReader(pathToJson);
var myJson = r.ReadToEnd();
// my/path/config/default.Business.Area.json
[...] do parsing here
Path.Combine uses the Path.PathSeparator and it checks whether the first path has already a separator at the end so it will not duplicate the separators. Additionally, it checks whether the path elements to combine have invalid chars.
See https://stackoverflow.com/a/32071002/4420355
There is a faster way of parsing json then Json.Net . If you are using .net core 3.0 or up then you can use the System.Text.Json nuget package to serialize or deserialize.
you need to add:
using System.Text.Json
And then you can serialize as:
var jsonStr = JsonSerializer.Serialize(model);
And Deserialize as:
var model = JsonSerializer.Deserialize(jsonStr);
This code can help you:
string _filePath = Path.GetDirectoryName(System.AppDomain.CurrentDomain.BaseDirectory);
JObject data = JObject.Parse(_filePath );
There is an easier way to get JSON from file or from the Web:
Json.Net.Curl
Install-Package Json.Net.Curl
// get JObject from local file system
var json = Json.Net.Curl.Get(#"data\JObjectUnitTest1.json");
var json = await Json.Net.Curl.GetAsync(#"data\JObjectUnitTest1.json")
// get JObject from Server
var json = await Json.Net.Curl.GetAsync("http://myserver.com/data.json");
GitHub Project
Nuget
With Cinchoo ETL, an open source library, parsing of very large JSON file is iterative and simple to use
1. Dynamic Method: - No POCO class required
string json = #"
[
{
""millis"": ""1000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
},
{
""millis"": ""2000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
}
]
";
using (var r = ChoJSONReader.LoadText(json))
{
foreach (var rec in r)
Console.WriteLine(rec.Dump());
}
Sample fiddle: https://dotnetfiddle.net/mo1qvw
2. POCO Method:
Define POCO class matching json attributes
public class Item
{
public int Millis { get; set; }
public string Stamp { get; set; }
public DateTime Datetime { get; set; }
public string Light { get; set; }
public float Temp { get; set; }
public float Vcc { get; set; }
}
Then using the parser to load the JSON as below
string json = #"
[
{
""millis"": ""1000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
},
{
""millis"": ""2000"",
""stamp"": ""1273010254"",
""datetime"": ""2010/5/4 21:57:34"",
""light"": ""333"",
""temp"": ""78.32"",
""vcc"": ""3.54""
}
]
";
using (var r = ChoJSONReader<Item>.LoadText(json))
{
foreach (var rec in r)
Console.WriteLine(ChoUtility.Dump(rec));
}
Sample fiddle: https://dotnetfiddle.net/fRWu0w
Disclaimer: I'm author of this library.

Categories