XML structuring and multiple xml based on data algorithm - c#

This is my table:
ItemsTable
Id, Codes, Price, Weight
I want to produce one or many xml files based on these rules using C#.
Within the xml there can not be more than 99 items, if it is more it should start a new xml file. There can not be more than 99 items in MyItems, if there are more it should start a new MyCollection.
<xml>
<MyCollections>
<!--- 0 to 99 MyCollection items allowed-->
<MyCollection>
<Code>6201110000</Code>
<MyItems>
<!--- 0 to 99 MyItems allowed -->
<MyItem>
<Sequence>1</Price>
<Price>10</Price>
<Weight>20</Weight>
</MyItem>
....
<MyItem>
<Sequence>99</Price>
<Price>300</Price>
<Weight>2</Weight>
</MyItem>
</Item>
</MyCollection>
<MyCollection>
<Code>6201110000</Code>
<MyItems>
<MyItem>
<Sequence>100</Price>
<Price>10</Price>
<Weight>20</Weight>
</MyItem>
.....
</MyCollection>

The loops is complicated. I think I got it right. I used Xml Linq.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string PATH = #"c:\temp\";
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(string));
dt.Columns.Add("Codes", typeof(string));
dt.Columns.Add("Price", typeof(decimal));
dt.Columns.Add("Weight", typeof(int));
dt.Rows.Add(new object[] { "1", "620111000", 1.00, 20 });
dt.Rows.Add(new object[] { "2", "610433000", 4.00, 30 });
dt.Rows.Add(new object[] { "3", "620111005", 7.00, 50 });
dt.Rows.Add(new object[] { "4", "620111006", 6.00, 60 });
dt.Rows.Add(new object[] { "5", "620111006", 5.00, 70 });
string xml = "<xml><MyCollections></MyCollections></xml>";
var groups = dt.AsEnumerable().OrderBy(x => x.Field<string>("Codes")).GroupBy(x => x.Field<string>("Codes")).ToList();
XDocument doc = XDocument.Parse(xml);
XElement myCollections = doc.Descendants("MyCollections").FirstOrDefault();
int sequence = 0;
int file_count = 1;
XElement myCollection = null;
XElement myItems = null;
string firstGroupName = "";
string code = "";
foreach (var group in groups)
{
code = group.Key;
//only add here if there is not 99 items in file
//other wise it will be added later
if (sequence < 99)
{
myCollection = new XElement("MyCollection", new XElement("Code", code));
myCollections.Add(myCollection);
myItems = new XElement("MyItems");
myCollection.Add(myItems);
firstGroupName = group.Key;
}
foreach (DataRow row in group)
{
sequence++;
if (sequence > 99)
{
if (firstGroupName == code)
{
doc.Save(PATH + code + "_" + file_count.ToString() + ".xml");
}
else
{
doc.Save(PATH + firstGroupName + "_" + code + "_" + file_count.ToString() + ".xml");
}
file_count++;
sequence = 1;
doc = XDocument.Parse(xml);
myCollections = doc.Descendants("MyCollections").FirstOrDefault();
myCollection = new XElement("MyCollection", new XElement("Code", code));
myCollections.Add(myCollection);
myItems = new XElement("MyItems");
myCollection.Add(myItems);
}
XElement myItem = new XElement("MyItem", new object[] {
new XElement("Sequence", sequence),
new XElement("Price", row.Field<decimal>("Price")),
new XElement("Weight", row.Field<int>("Weight"))
});
myItems.Add(myItem);
}
}
if (myItems.Elements("MyItem") != null)
{
if (firstGroupName == code)
{
doc.Save(PATH + code + "_" + file_count.ToString() + ".xml");
}
else
{
doc.Save(PATH + firstGroupName + "_" + code + "_" + file_count.ToString() + ".xml");
}
}
}
}
}

You can query your table with take ans skip.
Let's say you have two methods:
private IList<string> Query(string s)
{
// Execute your SQL query
}
private void SerializeItems(IList<object> itemsList)
{
//Serialize your itemslist to an xml file
}
You can read 99 item and serialize them:
var offset = 99;
var readMore = true;
while (readMore)
{
var itemsList = Query(
$#"SELECT ... FROM ... OFFSET {offset} ROWS FETCH NEXT 99 ROWS ONLY;");
SerializeItems(itemsList);
offset += 99;
readMore = itemsList.Count == 99;
}

Related

How can I bind a XML web response into a dataGridView in a Windows Forms using C#?

I am working in a Plugin in Windows Forms in C#. I basically send a soap request and get a response in xml. I am simply returning the response in a message box right now but I am trying to bind the response into a datagrid view. How can I do that?
This is what my SOAP req looks like the following.
private void getEntitySummary(long entityID)
{
//Create client
myAPI.ResolvingBinding resol = new myAPI.ResolvingBinding();
//Create parameters
myAPI.EntityID[] entityIDArr = new myAPI.EntityID[1];
myAPI.EntityID entityIDitem = new myAPI.EntityID();
entityIDitem.entityID = entityID;
entityIDArr[0] = entityIDitem;
myAPI.DepthSpecifier depth = new myAPI.DepthSpecifier();
myAPI.CustomInformation customInfo = new myAPI.CustomInformation();
//Make request
myAPI.EntitySummary[] entitySummaryRes = resol.getEntitySummaries(entityIDArr, depth, customInfo);
//Handle response
foreach (myAPI.EntitySummary e in entitySummaryRes)
{
string firstName = e.bestName?.givenName;
string surName = e.bestName?.surname;
string streetName = e.bestAddress?.street1;
string city = e.bestAddress?.city;
string state = e.bestAddress?.state;
string country = e.bestAddress?.country;
string address = (streetName + " " + city + " " + state + " " + country).Trim();
string email = e.bestEmail?.emailAddress;
string number = e.bestNumber?.numberValue;
MessageBox.Show(firstName + " " + surName + ", " + address +", " + number );
// need datagridView instead of a msg box here
}
}
The code works fine and gives me response in xml format in the background. It looks something like the following:
<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:errdetail="http://rr.eas.ibm.com/EntityResolver/ErrorDetail" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getEntitySummariesResponse xmlns="http://services/">
<entitySummaries xmlns="">
<entityID>101010</entityID>
<nameCount>3</nameCount>
<numberCount>7</numberCount>
<addressCount>9</addressCount>
<emailCount>0</emailCount>
<attributeCount>6</attributeCount>
<accountCount>21</accountCount>
<roleAlertCount>447</roleAlertCount>
<relationshipCount>19</relationshipCount>
<eventAlertCount>0</eventAlertCount>
<bestName>
<identityHandle>
<internalID>222</internalID>
<externalID>333John</externalID>
<dataSourceCode>neutral</dataSourceCode>
<externalReference>333John</externalReference>
</identityHandle>
<timestamp>2016-06-28T12:24:21</timestamp>
<lastModifiedTimestamp>2010-05-28T12:24:21</lastModifiedTimestamp>
<nameID>444</nameID>
<nameTypeCode>M</nameTypeCode>
<givenName>John</givenName>
<surname>Doe</surname>
<culture>culture</culture>
</bestName>
<bestAddress>
<identityHandle>
<internalID>222</internalID>
<externalID>333John</externalID>
<dataSourceCode>neutral</dataSourceCode>
<externalReference>333John</externalReference>
</identityHandle>
<timestamp>2010-05-28T12:24:21</timestamp>
<lastModifiedTimestamp>2010-05-28T12:24:21</lastModifiedTimestamp>
<addressID>395116</addressID>
<addressTypeCode>H</addressTypeCode>
<street1>1111 East St</street1>
<city>Pikesville</city>
<state>AL</state>
<country>USA</country>
</bestAddress>
<bestNumber>
<identityHandle>
<internalID>222</internalID>
<externalID>333John</externalID>
<dataSourceCode>neutral</dataSourceCode>
<externalReference>333John</externalReference>
</identityHandle>
<timestamp>2010-05-28T12:24:21</timestamp>
<lastModifiedTimestamp>2010-05-28T12:24:21</lastModifiedTimestamp>
<numberID>6666</numberID>
<numberTypeID>2</numberTypeID>
<numberValue>123-45-6789</numberValue>
</bestNumber>
<bestAttribute xmlns:ns2="http://entity/results/" xsi:type="ns2:CharacteristicDetail">
<identityHandle>
<internalID>222</internalID>
<externalID>333John</externalID>
<dataSourceCode>neutral</dataSourceCode>
<externalReference>333John</externalReference>
</identityHandle>
<timestamp>2010-05-28T12:24:21</timestamp>
<lastModifiedTimestamp>2010-05-28T12:24:21</lastModifiedTimestamp>
<characteristicID>8888</characteristicID>
<characteristicValue>M</characteristicValue>
<characteristicTypeID>2</characteristicTypeID>
</bestAttribute>
</entitySummaries>
</getEntitySummariesResponse>
</soapenv:Body> </soapenv:Envelope>
In short, I need to bind, the name, address, and number in a dataGridView.
According to the above response, it would be :
I tried the following method but I don't understand how to get the xml response into my datagrid:
DataSet ds = new DataSet();
XmlTextReader reader = new XmlTextReader(new StringReader(xml));
ds.ReadXml(reader);
dataGridView1.DataSource = ds;
Any help would be greatly appreciated!
The ReadXml won't work because the number of nested levels on xml is too deep. You end up with lots of datatables with the data fragmented with no way of putting the data back into usable format. I will work on a solution. Will take me a few minutes.
I parsed some of the code to get a flat result. See code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
string response = File.ReadAllText(FILENAME);
XDocument doc = XDocument.Parse(response);
XElement root = doc.Root;
XNamespace ns = root.GetDefaultNamespace();
DataTable dt = new DataTable();
dt.Columns.Add("entityID", typeof(string));
dt.Columns.Add("nameCount", typeof(int));
dt.Columns.Add("numberCount", typeof(int));
dt.Columns.Add("addressCount", typeof(int));
dt.Columns.Add("emailCount", typeof(int));
dt.Columns.Add("attributeCount", typeof(int));
dt.Columns.Add("accountCount", typeof(int));
dt.Columns.Add("roleAlertCount", typeof(int));
dt.Columns.Add("relationshipCount", typeof(int));
dt.Columns.Add("eventAlertCount", typeof(int));
dt.Columns.Add("bestName_internalID", typeof(int));
dt.Columns.Add("bestName_externalID", typeof(string));
dt.Columns.Add("bestName_dataSourceCode", typeof(string));
dt.Columns.Add("bestName_externalReference", typeof(string));
dt.Columns.Add("bestName_timestamp", typeof(DateTime));
dt.Columns.Add("bestName_lastModifiedTimestamp", typeof(DateTime));
dt.Columns.Add("bestName_nameID", typeof(int));
dt.Columns.Add("bestName_nameTypeCode", typeof(string));
dt.Columns.Add("bestName_givenName", typeof(string));
dt.Columns.Add("bestName_surname", typeof(string));
dt.Columns.Add("bestName_culture", typeof(string));
List<XElement> entitySummaries = root.Descendants(ns + "entitySummaries").ToList();
foreach (XElement entitySummary in entitySummaries)
{
DataRow newRow = dt.Rows.Add();
string entityID = (string)entitySummary.Element(ns + "entityID");
int nameCount = (int)entitySummary.Element(ns + "nameCount");
int numberCount = (int)entitySummary.Element(ns + "numberCount");
int addressCount = (int)entitySummary.Element(ns + "addressCount");
int emailCount = (int)entitySummary.Element(ns + "emailCount");
int attributeCount = (int)entitySummary.Element(ns + "attributeCount");
int accountCount = (int)entitySummary.Element(ns + "accountCount");
int roleAlertCount = (int)entitySummary.Element(ns + "roleAlertCount");
int relationshipCount = (int)entitySummary.Element(ns + "relationshipCount");
int eventAlertCount = (int)entitySummary.Element(ns + "eventAlertCount");
XElement bestName = entitySummary.Element(ns + "bestName");
int internalID = (int)bestName.Descendants(ns + "internalID").FirstOrDefault();
string externalID = (string)bestName.Descendants(ns + "externalID").FirstOrDefault();
string dataSourceCode = (string)bestName.Descendants(ns + "dataSourceCode").FirstOrDefault();
string externalReference = (string)bestName.Descendants(ns + "externalReference").FirstOrDefault();
DateTime timestamp = (DateTime)bestName.Element(ns + "timestamp");
DateTime lastModifiedTimestamp = (DateTime)bestName.Element(ns + "lastModifiedTimestamp");
int nameID = (int)bestName.Element(ns + "nameID");
string nameTypeCode = (string)bestName.Element(ns + "nameTypeCode");
string givenName = (string)bestName.Element(ns + "givenName");
string surname = (string)bestName.Element(ns + "surname");
string culture = (string)bestName.Element(ns + "culture");
newRow.ItemArray = new object[] {
entityID,
nameCount,
numberCount,
addressCount,
emailCount,
attributeCount,
accountCount,
roleAlertCount,
relationshipCount,
eventAlertCount,
internalID,
externalID,
dataSourceCode,
externalReference,
timestamp,
lastModifiedTimestamp,
nameID,
nameTypeCode,
givenName,
surname,
culture
};
}//end foreach
datagridview1.Datasource = dt;
}
}
}

Create xml file with dataset like this format

Create xml file with dataset like this format-
-<_XPXML Note="" CrtTime="" CN="" DBN="" Srv="" Ver="" AppId="" Class="" IC="" Stock="" Desc="">
<_InvTrans IC="010006" Stock="1" Desc="2 " OppKind="1" Amount="744" Batch="6" Mat="108208"/>
<_InvTrans IC="010006" Stock="1" Desc="2 " OppKind="1" Amount="744" Batch="6" Mat="108208"/>
<_InvTrans IC="010006" Stock="1" Desc="2 " OppKind="1" Amount="744" Batch="6" Mat="108208"/>
</_XPXML>
If you want to create some custom xml that you can use XElement.
How to create <_XPXML Note="" CrtTime="" CN="" DBN="" Srv="" Ver="" AppId="" Class="" IC="" Stock="" Desc="">?
You can use the code below to create.
var node=new XElement("_XPXML ");
node.SetAttributeValue("Note","");
node.SetAttributeValue("CrtTime","");
// ...
// please write the Attribute
doc.Root.Add(node);
Before you use the code that you should new doc as this code
XDocument doc = new XDocument();
And you should add using System.Xml.Linq in file top.
After you set the attribute that you can save it to file.
doc.Save(xx);
The example:
XDocument doc = new XDocument();
XElement node = new XElement("_XPXML");
node.SetAttributeValue("Note", "");
var invTrans = new XElement("_InvTrans");
node.Add(invTrans);
invTrans.SetAttributeValue("IC", "010006");
doc.Add(node);
StringBuilder str = new StringBuilder();
TextWriter stream = new StringWriter(str);
doc.Save(stream);
The str is
<?xml version="1.0" encoding="utf-16"?>
<_XPXML Note="">
<_InvTrans IC="010006" />
</_XPXML>
Try following using xml linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication23
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable("_InvTrans");
dt.Columns.Add("IC", typeof(string));
dt.Columns.Add("Stock", typeof(string));
dt.Columns.Add("Desc", typeof(string));
dt.Columns.Add("OppKind", typeof(string));
dt.Columns.Add("Amount", typeof(string));
dt.Columns.Add("Batch", typeof(string));
dt.Columns.Add("Mat", typeof(string));
dt.Rows.Add(new object[] { "010006", "1", "2 ", "1", "744", "6", "108208" });
dt.Rows.Add(new object[] { "010006", "1", "2 ", "1", "744", "6", "108208" });
dt.Rows.Add(new object[] { "010006", "1", "2 ", "1", "744", "6", "108208" });
GetXmlTable(dt);
}
static XElement GetXmlTable(DataTable dt)
{
string tableName = dt.TableName;
XElement table = new XElement(tableName);
string[] columnNames = dt.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToArray();
foreach (DataRow row in dt.AsEnumerable())
{
XElement xRow = new XElement("_InvTrans");
table.Add(xRow);
foreach (string columnName in columnNames)
{
xRow.Add(new XAttribute(columnName, row[columnName]));
}
}
return table;
}
}
}

Caml query return text only

I need my caml query to return only text, e.g. now it's returning me this
<div class=ExternalClass104BBsdfEEDCEdddsf48C68C18AsdfB056FBsdfAB805>Real Content.....</div>
When I want Real Content..... only. Code I am using is,
using System;
using System.Data;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
namespace SPSandeep.ConsoleApplication
{
class Program
{
public static DataTable dt = null;
static void Main (string[] args)
{
using (SPSite site = new SPSite("http://ws2003/credit"))
{
using (SPWeb web = site.OpenWeb())
{
SPList mainList = web.Lists["Team Discussion"];
foreach (SPListItem folder in mainList.Folders)
{
Console.WriteLine("Folder Name: " + folder.Name);
Console.WriteLine("Folder ID: " + folder.ID);
// Read body of attachment
Console.WriteLine("Body: " + folder.Fields["Body"].GetFieldValueAsText(folder["Body"]));
Console.WriteLine("Threading: " + folder.Fields["Threading"].GetFieldValueAsText(folder["Threading"]).Length);
Console.WriteLine("=============================");
RenderAllReponses(mainList, folder.ID);
Console.WriteLine("=============================");
}
}
}
Console.ReadKey();
}
public static void RenderAllReponses (SPList mainList, int parentID)
{
SPQuery query = new SPQuery();
query.Query = string.Format("<Where><Eq><FieldRef Name='ParentFolderId' /><Value Type='Number'>{0}</Value></Eq></Where>", parentID.ToString());
query.ViewFields = #"<FieldRef Name='ows_ParentFolderId' /><FieldRef Name='Threading' /><FieldRef Name='Title' /><FieldRef Name='ID' /><FieldRef Name='Body' />";
query.ViewAttributes = "Scope='RecursiveAll'";
dt = mainList.GetItems(query).GetDataTable();
if (null != dt)
{
foreach (DataRow listItem in dt.Rows)
{
if (listItem["Threading"].ToString().Length == 56)
{
RenderResponse(listItem, 56);
}
}
}
}
public static void RenderResponse (DataRow listItem1, int length)
{
DataRow[] listItems = dt.Select(string.Format("Threading Like '{0}*' AND LEN(Threading)={1}", listItem1["Threading"], length));
foreach (DataRow item in listItems)
{
Console.WriteLine("Item DisplayName: " + item["Title"]); // Returns Title of Discussion
Console.WriteLine("List ID: " + item["ID"] );
//Console.WriteLine("List Folder ID: " + listItem["ParentFolderId"] + "<BR>"); // Returns ID of Parent Discussion
Console.WriteLine("Body: " + item["Body"]);
Console.WriteLine("Threading: " + item["Threading"].ToString().Length );
int newLength = length + 10;
RenderResponse(item, newLength);
}
}
}
}

Converting a csv file to json using C#

I was wondering if someone's written a utility to convert a CSV file to Json using C#. From a previous question on stackoverflow, I'm aware of this nice utility - https://github.com/cparker15/csv-to-json and at the moment I plan to refer to it but an existing C# implementation would be very helpful! Thanks!
If you can use System.Web.Extensions, something like this could work:
var csv = new List<string[]>(); // or, List<YourClass>
var lines = System.IO.File.ReadAllLines(#"C:\file.txt");
foreach (string line in lines)
csv.Add(line.Split(',')); // or, populate YourClass
string json = new
System.Web.Script.Serialization.JavaScriptSerializer().Serialize(csv);
You might have more complex parsing requirements for the csv file and you might have a class that encapsulates the data from one line, but the point is that you can serialize to JSON with one line of code once you have a Collection of lines.
Cinchoo ETL - an open source library available to do the conversion of CSV to JSON easily with few lines of code
For a sample CSV:
Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
Sample code,
string csv = #"Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";
StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
.WithFirstLineHeader()
)
{
using (var w = new ChoJSONWriter(sb))
w.Write(p);
}
Console.WriteLine(sb.ToString());
Output JSON:
[
{
"Id": "1",
"Name": "Tom",
"City": "NY"
},
{
"Id": "2",
"Name": "Mark",
"City": "NJ"
},
{
"Id": "3",
"Name": "Lou",
"City": "FL"
},
{
"Id": "4",
"Name": "Smith",
"City": "PA"
},
{
"Id": "5",
"Name": "Raj",
"City": "DC"
}
]
Sample fiddle: https://dotnetfiddle.net/pclnsT
Checkout CodeProject article for some additional help.
UPDATE:
If your CSV file has duplicate column names or no names, please use the below steps to produce the JSON file
string csv = #"Id, Name,
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";
StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
.WithField("Id", position: 1)
.WithField("Name", position: 2)
.WithField("City", position: 3)
.WithFirstLineHeader(true)
)
{
using (var w = new ChoJSONWriter(sb))
w.Write(p);
}
Console.WriteLine(sb.ToString());
Sample fiddle: https://dotnetfiddle.net/pP5Du6
Disclaimer: I'm the author of this library.
I used Dictionary and returned json using newtonsoft
public string ConvertCsvFileToJsonObject(string path)
{
var csv = new List<string[]>();
var lines = File.ReadAllLines(path);
foreach (string line in lines)
csv.Add(line.Split(','));
var properties = lines[0].Split(',');
var listObjResult = new List<Dictionary<string, string>>();
for (int i = 1; i < lines.Length; i++)
{
var objResult = new Dictionary<string, string>();
for (int j = 0; j < properties.Length; j++)
objResult.Add(properties[j], csv[i][j]);
listObjResult.Add(objResult);
}
return JsonConvert.SerializeObject(listObjResult);
}
Install Nuget package NewtonSoft.Json
Add reference dll Microsoft.VisualBasic
using System.Linq;
using Newtonsoft.Json;
using Microsoft.VisualBasic.FileIO;
using System.IO;
using System;
using System.Collections.Generic;
using System.Globalization;
namespace Project
{
public static class Program
{
public static void Main(string[] args)
{
string CSVpath = #"D:\New Folder\information.csv";
string analyticsData = ReadFile(CSVpath);
}
private static string ReadFile(string filePath)
{
string payload = "";
try
{
if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath) && Path.GetExtension(filePath).Equals(".csv", StringComparison.InvariantCultureIgnoreCase))
{
string[] lines = File.ReadAllLines(filePath);
if (lines != null && lines.Length > 1)
{
var headers = GetHeaders(lines.First());
payload = GetPayload(headers, lines.Skip(1));
}
}
}
catch (Exception exp)
{
}
return payload;
}
private static IEnumerable<string> GetHeaders(string data)
{
IEnumerable<string> headers = null;
if (!string.IsNullOrWhiteSpace(data) && data.Contains(','))
{
headers = GetFields(data).Select(x => x.Replace(" ", ""));
}
return headers;
}
private static string GetPayload(IEnumerable<string> headers, IEnumerable<string> fields)
{
string jsonObject = "";
try
{
var dictionaryList = fields.Select(x => GetField(headers, x));
jsonObject = JsonConvert.SerializeObject(dictionaryList);
}
catch (Exception ex)
{
}
return jsonObject;
}
private static Dictionary<string, string> GetField(IEnumerable<string> headers, string fields)
{
Dictionary<string, string> dictionary = null;
if (!string.IsNullOrWhiteSpace(fields))
{
var columns = GetFields(fields);
if (columns != null && headers != null && columns.Count() == headers.Count())
{
dictionary = headers.Zip(columns, (x, y) => new { x, y }).ToDictionary(item => item.x, item => item.y);
}
}
return dictionary;
}
public static IEnumerable<string> GetFields(string line)
{
IEnumerable<string> fields = null;
using (TextReader reader = new StringReader(line))
{
using (TextFieldParser parser = new TextFieldParser(reader))
{
parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); fields = parser.ReadFields();
}
}
return fields;
}
}
}
Taking only a dependency on Newtonsoft.Json, here's a helper method given an array of CSV lines, the first one being the header.
public static IEnumerable<JObject> CsvToJson(IEnumerable<string> csvLines)
{
var csvLinesList = csvLines.ToList();
var header = csvLinesList[0].Split(',');
for (int i = 1; i < csvLinesList.Count; i++)
{
var thisLineSplit = csvLinesList[i].Split(',');
var pairedWithHeader = header.Zip(thisLineSplit, (h, v) => new KeyValuePair<string, string>(h, v));
yield return new JObject(pairedWithHeader.Select(j => new JProperty(j.Key, j.Value)));
}
}
I use ChoETL:
using ChoETL;
using System.IO;
public class FromCSVtoJSON
{
public FromCSVtoJSON()
{
}
public void convertFile(string inputFile, string outputFile)
{
using (var writer = new ChoJSONWriter(outputFile))
{
using (var reader = new ChoCSVReader(inputFile).WithFirstLineHeader())
{
writer.Write(reader);
}
}
}
}
From that same SO answer, there is a link to this post.
CsvToJson extention method
/// <summary>
/// Converts a CSV string to a Json array format.
/// </summary>
/// <remarks>First line in CSV must be a header with field name columns.</remarks>
/// <param name="value"></param>
/// <returns></returns>
public static string CsvToJson(this string value)
{
// Get lines.
if (value == null) return null;
string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length < 2) throw new InvalidDataException("Must have header line.");
// Get headers.
string[] headers = lines.First().SplitQuotedLine(new char[] { ',' }, false);
// Build JSON array.
StringBuilder sb = new StringBuilder();
sb.AppendLine("[");
for (int i = 1; i < lines.Length; i++)
{
string[] fields = lines[i].SplitQuotedLine(new char[] { ',', ' ' }, true, '"', false);
if (fields.Length != headers.Length) throw new InvalidDataException("Field count must match header count.");
var jsonElements = headers.Zip(fields, (header, field) => string.Format("{0}: {1}", header, field)).ToArray();
string jsonObject = "{" + string.Format("{0}", string.Join(",", jsonElements)) + "}";
if (i < lines.Length - 1)
jsonObject += ",";
sb.AppendLine(jsonObject);
}
sb.AppendLine("]");
return sb.ToString();
}
There appears to be an issue with where some methods called within the above extension live (see the comments of the original blog post), but it should get you most of the way there.
EDIT Here is another SO answer about splitting a CSV line. You could use one of the suggested regex solutions to create your own SplitQuotedLine method:
public static string SplitQuotedLine(this string value, char separator, bool quotes) {
// Use the "quotes" bool if you need to keep/strip the quotes or something...
var s = new StringBuilder();
var regex = new Regex("(?<=^|,)(\"(?:[^\"]|\"\")*\"|[^,]*)");
foreach (Match m in regex.Matches(value)) {
s.Append(m.Value);
}
return s.ToString();
}
I did not test the above, so forgive me if I made any errors.
Also, it would appear that Zip is a LINQ extension method, so that takes care of that problem.
Here's mine.. It can parse 9k CSV records in centuries. LOL
class CSVTOJSON
{
public string ConvertToJSON()
{
string json = string.Empty;
string csv = string.Empty;
using (StreamReader reader = new StreamReader("data.csv"))
{
csv = reader.ReadToEnd();
}
string[] lines = csv.Split(new string[] { "\n" }, System.StringSplitOptions.None);
if (lines.Length > 1)
{
// parse headers
string[] headers = lines[0].Split(',');
StringBuilder sbjson = new StringBuilder();
sbjson.Clear();
sbjson.Append("[");
// parse data
for (int i = 1; i < lines.Length; i++)
{
if (string.IsNullOrWhiteSpace(lines[i])) continue;
if (string.IsNullOrEmpty(lines[i])) continue;
sbjson.Append("{");
string[] data = lines[i].Split(',');
for (int h = 0; h < headers.Length; h++)
{
sbjson.Append(
$"\"{headers[h]}\": \"{data[h]}\"" + (h < headers.Length - 1 ? "," : null)
);
}
sbjson.Append("}" + (i < lines.Length - 1 ? "," : null));
}
sbjson.Append("]");
json = sbjson.ToString();
}
return json;
}
}
But it works.
console log:
Converting CSV to JSON
CSV has 9486 data
Total duration converting CSV to JSON: 00:00:00.0775373
Small variation to the solution by bc3tech i.e. avoiding external dependencies (on Newtonsoft.Json), and instead using System.Text.Json (dotnet core 3+)
public static IEnumerable<string> CsvToJson(string fileName, char delim = '|')
{
var lines = File.ReadLines(fileName);
var hdr = new List<string>(lines.First().Trim().Split(delim));
foreach (var l in lines.Skip(1).Where(l => (l.Trim() != String.Empty)))
{
var val = l.Trim().Split(delim);
var ds = hdr.Zip(val, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v);
yield return JsonSerializer.Serialize(ds);
}
}
I can see most people simply assume parsing CSV file is to simply split comma delimiter between each column, but the following format is still a valid CSV
"aaa","bbb","ccc"
"z, z",yyy,xxx
There is a nice class hidden within Microsoft.VisualBasic.FileIO to handle CSV file format correctly. I combine this with JSON.NET came up with the solution.
public static string? CsvToJson(string input, string delimiter)
{
using (TextFieldParser parser = new TextFieldParser(
new MemoryStream(Encoding.UTF8.GetBytes(input))))
{
parser.Delimiters = new string[] { delimiter };
string[]? headers = parser.ReadFields();
if (headers == null) return null;
string[]? row;
string comma = "";
var sb = new StringBuilder((int)(input.Length * 1.1));
sb.Append("[");
while ((row = parser.ReadFields()) != null)
{
var dict = new Dictionary<string, object>();
for (int i = 0; row != null && i < row.Length; i++)
dict[headers[i]] = row[i];
var obj = JsonConvert.SerializeObject(dict);
sb.Append(comma + obj);
comma = ",";
}
return sb.Append("]").ToString();
}
}
Usage
var str = #"Header1,""Header,,2 "",Data3
1,444.00, ""Liang, Jerry""
0,""5,550"",Jerry
";
var json = CsvToJson(str, ",");
Result
[
{
"Header1": "1",
"Header,,2": "444.00",
"Data3": "Liang, Jerry"
},
{
"Header1": "0441",
"Header,,2": "5,550",
"Data3": "Jerry"
}
]
I looked for the answer for this question finally i solved it by using Dictionary
public static void CreateJsonFromCSV()
{
string path = "C:\\Users\\xx\\xx\\xx\\xx\\lang.csv";
string textFilePath = path;
const Int32 BufferSize = 128;
using (var fileStream = File.OpenRead(textFilePath))
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
{
String line;
Dictionary<string, string> jsonRow = new Dictionary<string, string>();
while ((line = streamReader.ReadLine()) != null)
{
string[] parts = line.Split(',');
string key_ = parts[0];
string value = parts[1];
if (!jsonRow.Keys.Contains(key_))
{
jsonRow.Add(key_, value );
}
}
var json = new JavaScriptSerializer().Serialize(jsonRow);
string path_ = "C:\\XX\\XX\\XX\\XX\\XX.csv";
File.WriteAllText(path_, json);
}
}
Make sure you add the below in web.config before you do parse large csv files.
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="50000000"/>
</webServices>
</scripting>
</system.web.extensions>
Try this:
StreamReader sr = new StreamReader(filePath);
while ((line = sr.ReadLine()) != null)
{
//Console.WriteLine(line);
string[] csv = line.Split(',');
var dictionary = new Dictionary<string, string>();
dictionary.Add("dispatching_base_number",csv[0]);
dictionary.Add("available_vehicles", csv[1]);
dictionary.Add("vehicles_in_trips", csv[2]);
dictionary.Add("Cancellations", csv[3]);
string jsonN = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(dictionary);
Console.WriteLine("Sending message: {0}",jsonN);
}
Try this and convert CSV to JSON object:
public static List<object> CsvToJson( string body, string[] column ) {
if ( string.IsNullOrEmpty( body ) ) return null;
string[] rowSeparators = new string[] { "\r\n" };
string[] rows = body.Split( rowSeparators, StringSplitOptions.None );
body = null;
if ( rows == null || ( rows != null && rows.Length == 0 ) ) return null;
string[] cellSeparator = new string[] { "," };
List<object> data = new List<object>( );
int clen = column.Length;
rows.Select( row => {
if ( string.IsNullOrEmpty( row ) ) return row;
string[] cells = row.Trim( ).Split( cellSeparator, StringSplitOptions.None );
if ( cells == null ) return row;
if ( cells.Length < clen ) return row;
Dictionary<object, object> jrows = new Dictionary<object, object>( );
for ( int i = 0; i < clen; i++ ) {
jrows.Add( column[i], cells[i]?.Trim( ) );
}
data.Add( jrows );
return row;
} ).ToList( );
rowSeparators = null; rows = null;
cellSeparator = null;
return data;
}
var data = CsvToJson("csv_input_str", new string[]{ "column_map" })
string jsonStr = new JavaScriptSerializer { MaxJsonLength = int.MaxValue }.Serialize( data );
First, load the csv file into datatable and serialize it to Json document. It uses OLEDB Provider that can parse the csv wisely,
Courtesy to Jim Scott, https://stackoverflow.com/a/1050278/6928056
Courtesy to K_B, https://stackoverflow.com/a/2979938/6928056
using System.Data;
using System.Data.OleDb;
using System.Globalization;
using System.IO;
using Newtonsoft.Json;
static string ConvertCsvToJson(string path, bool isFirstRowHeader)
{
string header = isFirstRowHeader ? "Yes" : "No";
string pathOnly = Path.GetDirectoryName(path);
string fileName = Path.GetFileName(path);
string sql = #"SELECT * FROM [" + fileName + "]";
using(OleDbConnection connection = new OleDbConnection(
#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly +
";Extended Properties=\"Text;HDR=" + header + "\""))
using(OleDbCommand command = new OleDbCommand(sql, connection))
using(OleDbDataAdapter adapter = new OleDbDataAdapter(command))
{
var dataTable = new DataTable();
dataTable.Locale = CultureInfo.CurrentCulture;
adapter.Fill(dataTable);
return JsonConvert.SerializeObject(dataTable, Formatting.Indented);
}
}
Simple method to convert flat csv file to a collection of simple json formatted objects. Works with files with header row on the first line. Part of this method was found somewhere else on SO.
Add reference to Microsoft.VisualBasic.
using Microsoft.VisualBasic.FileIO;
public static StringBuilder ReadCsv()
{
var path = #"X:\...\input.csv";
using (TextFieldParser csvParser = new TextFieldParser(path))
{
csvParser.CommentTokens = new string[] { "#" };
//Remember to use your own separator
csvParser.SetDelimiters(new string[] { ";" });
csvParser.HasFieldsEnclosedInQuotes = false;
StringBuilder json = new StringBuilder();
string[] colNames = new string[0];
string[] fields = new string[0];
json.Append("[");
int counter = 0;
while (!csvParser.EndOfData)
{
if (counter == 0)
{
//Read properies' names
colNames = csvParser.ReadFields();
counter++;
Console.WriteLine($"{colNames.Length} columns detected!");
}
else
{
// Read current line fields, pointer moves to the next line.
// Read the properties' values
fields = csvParser.ReadFields();
json.Append("{");
for (int i = 0; i < colNames.Length; i++)
{
json.Append($"\"{colNames[i]}\":{TryParse(fields[i])}");
if (i != colNames.Length - 1)
{
json.Append(",");
}
}
json.Append("},");
Console.WriteLine($"Writing record nr.: {counter}");
counter++;
}
}
json.Length--; //Remove trailing comma
json.Append("]");
return json;
}
}
string TryParse(string s)
{
if (string.IsNullOrEmpty(s)) return "null";
//Remember to set your decimal character here!
if (s.Contains('.'))
{
double dResult;
//This works on my computer, could be different on your machine
if (double.TryParse(s, NumberStyles.AllowDecimalPoint,
CultureInfo.InvariantCulture, out dResult))
return dResult.ToString(CultureInfo.InvariantCulture);
}
else
{
int intResult;
if (int.TryParse(s, out intResult))
return intResult.ToString(CultureInfo.InvariantCulture);
}
return "\"" + s + "\"";
}
This should give you a simple list of json objects.
If you are looking for a C# only solution this might work for you. I recently face the same issue and created this method to overcome.
public static string ConvertToJsonStructure(string csvDataAsString, char delimiter = ',', bool hasHeaders = true)
{
var output = string.Empty;
if (string.IsNullOrEmpty(csvDataAsString))
return "{}";
var rows = csvDataAsString.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
var headers = hasHeaders ? rows[0].Split(delimiter) : null;
var colCount = headers?.Length;
var jsonStart = "{";
var jsonEnd = "}";
if (rows.Length > (1 + (hasHeaders ? 1 : 0)))
{
jsonStart = "[";
jsonEnd = "]";
}
output = output + jsonStart;
int startIndex = hasHeaders ? 1 : 0;
for (int i = startIndex; i < rows.Length; i++)
{
var cols = rows[i].Split(delimiter);
if (colCount == null)
colCount = cols.Length;
var tempJson = "{";
for (int j = 0; j < colCount.Value; j++)
{
if (hasHeaders)
tempJson = tempJson + $"\"{headers[j]}\":";
var isNumber = Regex.IsMatch(cols[j], #"^\d + $");
var val = isNumber ? cols[j] : $"\"{cols[j]}\"";
tempJson = tempJson + val;
if (j < colCount.Value - 1)
tempJson = tempJson + ",";
}
tempJson = tempJson + "}";
if (i < rows.Length - 1)
tempJson = tempJson + ",";
output = output + tempJson;
}
output = output + jsonEnd;
return output;
}
}
You need to pass your content as string to this method and you can do something like this to read your csv
var csvAsString = File.ReadAllText("TestCsv.csv");
var result = CsvToJson.ConvertToJsonStructure(csvAsString);

LINQ to read XML file and print results

I got the following XML file (Data.xml):
<root>
<sitecollection name="1A">
<site name="1B">
<maingroup name="1C">
<group name="1D"> </group>
</maingroup>
</site>
</sitecollection>
<sitecollection name="2A">
<site name="2B">
<maingroup name="2C">
<group name="2D"> </group>
</maingroup>
</site>
</sitecollection>
</root>
And I need to print all the all the child elements in this format:
1A
1B
1C
1D
2A
2B
2C
2D
I have the following code so far which needs some adjustment. I could also change it completely if there's an easier method. Thanks for your help
class xmlreader
{
public static void Main()
{
// Xdocument to read XML file
XDocument xdoc = XDocument.Load("Data.xml");
var result = new System.Text.StringBuilder();
var lv1s = from lv1 in xdoc.Descendants("sitecollection")
select new
{
sitecollection = lv1.Attribute("name").Value,
maingroup = lv1.Descendants("group")
};
var lv2s = from lv2 in xdoc.Descendants("site")
select new
{
site = lv2.Attribute("name").Value,
sitetittle = lv2.Descendants()
};
var lv3s = from lv3 in xdoc.Descendants("maingroup")
select new
{
maingroup = lv3.Attribute("name").Value,
};
var lv4s = from lv4 in xdoc.Descendants("group")
select new
{
grouppage = lv4.Attribute("name").Value,
};
// Loop to print results
foreach (var lv1 in lv1s)
{
result.AppendLine(lv1.sitecollection);
foreach (var lv2 in lv2s)
{
result.AppendLine(" " + lv2.Attribute("name").Value);
foreach (var lv3 in lv3s)
{
result.AppendLine(" " + lv3.Attribute("name").Value);
foreach (var lv4 in lv4s)
{
result.AppendLine(" " + lv4.Attribute("name").Value);
}
}
}
}
}
}
With such a uniform hierarchy, recursion can do the job with a lot less code:
void PrintNames(StringBuilder result, string indent, XElement el)
{
var attr = el.Attributes("name");
if (attr != null)
{
result.Append(indent);
result.Append(attr.Value);
result.Append(System.Environment.NewLine);
}
indent = indent + " ";
foreach(var child in el.Elements())
{
PrintNames(result, indent, child);
}
}
...
var sb = new StringBuilder();
PrintNames(sb, String.Empty, xdoc.Root);
How about the following, find all elements with a name attribute, then add spaces based on their depth.
var result = new System.Text.StringBuilder();
var namedElements = doc.Descendants().Where(el => el.Attributes("name")!=null);
foreach(var el in namedElements)
{
int depth = el.Ancestors().Count();
for (int i=0;i<depth;i++)
result.Append(" ");
result.Append(el.Attributes("name").Value);
result.Append(System.Environment.NewLine);
}
NOTE: The above is from memory, so please check the syntax!

Categories