Convert SQLDataReader results to JSON, with nested JSON objects - c#

I have a C# application which retrieves an SQL result set in the following format:
customer_id date_registered date_last_purchase loyalty_points
1 2017-01-01 2017-05-02 51
2 2017-01-23 2017-06-21 124
...
How can I convert this to a JSON string, such that the first column (customer_id) is a key, and all other subsequent columns are values within a nested-JSON object for each customer ID?
Example:
{
1: {
date_registered: '2017-01-01',
date_last_purchase: '2017-05-02',
loyalty_points: 51,
...
},
2: {
date_registered: '2017-01-23',
date_last_purchase: '2017-06-21',
loyalty_points: 124,
...
},
...
}
Besides date_registered, date_last_purchase, and loyalty_points, there may be other columns in the future so I do not want to refer to these column names specifically. Therefore I have already used the code below to fetch the column names, but am stuck after this.
SqlDataReader sqlDataReader = sqlCommand.ExecuteReader();
var columns = new List<string>();
for (var i = 0; i < sqlDataReader.FieldCount; i++)
{
columns.Add(sqlDataReader.GetName(i));
}
while (sqlDataReader.Read())
{
rows.Add(columns.ToDictionary(column => column, column => sqlDataReader[column]));
}

You could use something like this to convert the data reader to a Dictionary<object, Dictionary<string, object>> and then use Json.NET to convert that to JSON:
var items = new Dictionary<object, Dictionary<string, object>>();
while (sqlDataReader.Read())
{
var item = new Dictionary<string, object>(sqlDataReader.FieldCount - 1);
for (var i = 1; i < sqlDataReader.FieldCount; i++)
{
item[sqlDataReader.GetName(i)] = sqlDataReader.GetValue(i);
}
items[sqlDataReader.GetValue(0)] = item;
}
var json = Newtonsoft.Json.JsonConvert.SerializeObject(items, Newtonsoft.Json.Formatting.Indented);
Update: JSON "names" are always strings, so used object and GetValue for the keys.

Related

Why is this ArrayList duplicating he rows

I am junior developer and I am trying to populate an ArrayList from a Dictionary. My problem is rather then adding a new record to the ArrayList it adds the new record but also overwrites the values for all the other values in the array.
So if I inspect the values as the ArrayList is being populated I see the values from the Dictionary as expected. But when that row is inserted into the ArrayList all of the existing rows are over written with the data from current Dictionary Row. So I end up with an ArrayList with several rows that are a duplicate of the last record added from the dictionary. My code is shown below. Can someone please tell me what am I doing wrong? Code below
ArrayList arrData = new ArrayList();
eSummary edata = new eSummary();
//Starts with the first 50 recods retrieved and adds them to the ArrayList. Loops thru to get remaining records
while (blnEmpty)
{
if (response.IsSuccessStatusCode)
{
string json = response.Content.ReadAsStringAsync().Result;
var jss = new JavaScriptSerializer();
var dict = jss.Deserialize<Dictionary<string, dynamic>>(json);
for (int i = 0; i < dict.Values.Sum(x => x.Count); i++)
{
foreach (var item in dict)
{
string checkId = (dict["data"][i]["Id"]);
edata.Id = dict["data"][i]["Id"];
edata.idExternal = (dict["data"][i]["idExternal"]) == null ? "" : (dict["data"][i]["idExternal"]);
edata.Type = "Video";
edata.ownerId = (dict["data"][i]["uploadedByOwnerId"]);
edata.dateUploaded = Convert.ToDateTime((dict["data"][i]["dateUploaded"]));
edata.durationSeconds = Convert.ToDouble((dict["data"][i]["durationSeconds"]));
edata.category = (dict["data"][i]["categories"]).Count < 1 ? string.Empty : (dict["data"][i]["categories"][0]);
edata.title = (dict["data"][i]["title"]) == string.Empty ? string.Empty : (dict["data"][i]["title"]);
edata.dateRecordStarted = Convert.ToDateTime((dict["data"][i]["dateRecordStart"]));
edata.DateAPIRan = DateTime.Now;
if (CheckAutoTag(checkId, dict["data"][i]["tags"]))
{
edata.AutoTagged = true;
}
else edata.AutoTagged = false;
arrData.Add(edata);
edata is a reference type. You keep updating the values of a single object within the loop.
You need to call new eSummary() and set the values on the new object and then add that to your list.
But do note, you should not be using ArrayList in modern c#. Use a List<eSummary> instead.

How can I get the value of 1 list depending on the value of another list

I'm trying to accomplish the following and would appreciate any help.
I have a CSV file containing 2 columns, 1 a list of names and the other a list of associated IDs.
Eg.
Test1, 00001
Test2, 00002
I have read these into my program into 2 lists.
1. nameList
2. idList
I have populated the datasource of a combobox with the values of nameList.
Now, when a name is selected in the combobox and a button pressed, I would like to get the ID. So if Test1 is selected, when the button is pressed 00001 is returned where as if Test2 is selected and the button pressed, 00002 is returned.
If it helps at all, this is what im currently using to populate the Lists.
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
List<string> nameList = new List<string>();
List<string> idList = new List<string>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
nameList.Add(values[0]);
idList.Add(values[1]);
}
cmbxName.DataSource = releaseNameList;
}
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
DataTable tbl = new DataTable();
tbl.Columns.Add("Name", typeof(string));
tbl.Columns.Add("ID", typeof(string));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
DataRow row = tbl.NewRow();
row["Name"]=values[0];
row["ID"] = values[1];
tbl.Rows.Add(row);
}
cmbxName.DisplayMember = "Name";
cmbxName.ValueMember = "ID";
cmbxName.DataSource = tbl;
}
You need something like that. Check properties DisplayMember and ValueMember of ComboBox.
So when the button is clicked and you want to take the Value not the display text.
protected void Button1_Click(object sender, EventArgs e)
{
string id = cmbxName.SelectedValue;
}
N.B: Your title is misleading !
Use a Dictionary
public void nameCSV()
{
var reader = new StreamReader(File.OpenRead(#"C:\temp\nameList.csv"));
Dictionary<string, int> userDict = new Dictionary<string, int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
userDict.Add(values[0], values[1]);
}
cmbxName.DataSource = userDict;
}
to call the value of a given user id just use the key. For example, 'Test1'
string Username = "Test1";
int userid;
userid = userDict[Username];
One note, dictionaries work better with numeric values as the key, not strings. Put in your case you're looking up the string name, so it should be this way. As long as the string lengths are short - aka no more than 10 characters. Peformance wise, you should be okay.

Read from files and parse SQL table

All the following must be done in C#. Parsing the SQL table (SQL Server) will be done using methods in System.Data.Odbc.
Let's assume I have two .csv files, fi1 and fi2. The first csv file has two columns id and val1, and the second csv has two columns as well, id and val2.
I would like to read the two files, and parse the output to one SQL table with the following columns: id, val1, val2.
The problem is that the two files may have different entries in the id columns: in other words, some id's may have a val1 value but no val2 value, and vice versa, or they might have both values.
The table should contain the union of the id columns in the two files.
Example:
File 1
File2
The way I would want the final SQL table to look like is this:
Note that each file might contain duplicates, and we would want to exclude the duplicates when parsing the SQL table.
The thought I had is to create two dictionaries, dict1 and dict2, where the key would be the id, and the value would be val1 and val2. Dictionaries will be used to make sure that duplicates are not included:
Dictionary<string, string> dict1 = new Dictionary<string, string>();
string[] header1 = new string[]{};
using (StreamReader rdr = new StreamReader(fi1))
{
header1 = rdr.ReadLine().Split(',');
while (!rdr.EndOfStream)
{
string ln = rdr.ReadLine();
string[] split_ln = ln.Split(',');
dict1.Add(split_ln[0], split_ln[1]);
}
}
Dictionary<string, string> dict2 = new Dictionary<string, string>();
string[] header2 = new string[]{};
using (StreamReader rdr = new StreamReader(fi2))
{
header2 = rdr.ReadLine().Split(',');
while (!rdr.EndOfStream)
{
string ln = rdr.ReadLine();
string[] split_ln = ln.Split(',');
dict2.Add(split_ln[0], split_ln[1]);
}
}
However, after adding each file to a dictionary, I am not sure how to match the id's of both dictionaries.
Would anyone have a good hint as to how to deal with this problem?
I would do atually do a list of tuples to hold the values here instead of a dictionary so that all the information is in one place rather than matching keys, each tuple corresponds to a table record
var dict = new List<Tuple<string, string, string>>();
using (StreamReader rdr = new StreamReader(fi1))
{
while (!rdr.EndOfStream)
{
string ln = rdr.ReadLine();
string[] split_ln = ln.Split(',');
dict.Add(new Tuple<string, string, string>(split_ln[0], split_ln[1],null));
}
}
using (StreamReader rdr = new StreamReader(fi2))
{
while (!rdr.EndOfStream)
{
string ln = rdr.ReadLine();
string[] split_ln = ln.Split(',');
if (dict.Any(item => item.Item1 == split_ln[0]))
{
var item = dict.Find(i => i.Item1 == split_ln[0]);
var newtuple = new Tuple<string, string, string>(item.Item1, item.Item2, split_ln[1]);
dict.Remove(item);
dict.Add(newtuple);
}
else
{
dict.Add(new Tuple<string, string, string>(split_ln[0],null,split_ln[1]));
}
}
}

C# Creating named object for each object in LDAP DirectorySearch to insert into SQL database

Ive created a Directory Searcher to pull multiple properties from each user.
objSearchADAM = new DirectorySearcher(objADAM);
objSearchADAM.PropertiesToLoad.Add("givenname");
objSearchADAM.PropertiesToLoad.Add("lastlogontimestamp");
ect...
objSearchResults = objSearchADAM.FindAll();
I then enumerate them, and convert the interger8 timestamp to standard date/time, and save to csv file with
List<string> timeProps = new List<string>() { "lastlogontimestamp", "accountexpires", "pwdlastset", "lastlogoff", "lockouttime", "maxstorage", "usnchanged", "usncreated", "usndsalastobjremoved", "usnlastobjrem", "usnsource" };
foreach (SearchResult objResult in objSearchResults)
{
objEntry = objResult.GetDirectoryEntry();
ResultPropertyCollection myResultProp = objResult.Properties;
foreach (string myKey in myResultProp.PropertyNames)
{
foreach (Object myCollection in myResultProp[myKey])
{
Object sample = myCollection;
if (timeProps.Contains(myKey))
{
String times = sample.ToString();
long ft = Int64.Parse(times);
DateTime date;
try
{
date = DateTime.FromFileTime(ft);
}
catch (ArgumentOutOfRangeException ex)
{
date = DateTime.MinValue;
Console.WriteLine("Out of range: " + ft);
Console.WriteLine(ex.ToString());
}
sample = date;
Console.WriteLine("{0}{1}", myKey.PadRight(25), sample);
objWriter.WriteLine("{0}{1}", myKey.PadRight(25), sample);
}
else
{
Console.WriteLine("{0}{1}", myKey.PadRight(25), sample);
objWriter.WriteLine("{0}{1}", myKey.PadRight(25), sample);
}
}
now i need to create an object for each user with the strings from each result that i can put into an SQL command ive built. where the LDAP query to SQL would be givenname = FirstName and lastlogontimestamp = LastLogon and so on.
StringBuilder sb = new StringBuilder();
sb.Append("INSERT INTO activedirectory.dimUserST (FirstName, LastName) VALUES (#FirstName, #LastName)");
loadStagingCommand.Parameters.AddWithValue("#FirstName", FirstName).DbType = DbType.AnsiString;
ect...
loadStagingCommand.CommandText = sb.ToString();
loadStagingCommand.ExecuteNonQuery();
i tried to use IDictionary in my first foreach (similar to code found here http://ideone.com/vChWD ) but couldn't get it to work. I read about IList and reflection, but im not sure how i could incorporate these.
UPDATE
I researched and found ExpandoObjects and attempted to write in code based off of what i saw in here Creating Dynamic Objects
however i run this new code I return "employeenumber System.Collections.Generic.List`1[System.Dynamic.ExpandoObject]"
if(employeeNumber.Contains(myKey))
{
string[] columnNames = { "EmployeeNumber" };
List<string[]> listOfUsers = new List<string[]>();
for (int i = 0; i < 10; i++)
{
listOfUsers.Add(new[] { myKey});
}
var testData = new List<ExpandoObject>();
foreach (string[] columnValue in listOfUsers)
{
dynamic data = new ExpandoObject();
for (int j = 0; j < columnNames.Count(); j++)
{
((IDictionary<String, Object>)data).Add(columnNames[j], listOfUsers[j]);
}
testData.Add(data);
Console.WriteLine("{0}{1}", myKey.PadRight(25), testData);
objWriter.WriteLine("{0}{1}", myKey.PadRight(25), testData);
}
}
I am obviously missing something here and cant seem to wrap my head around what the problem is. I might even be going about this the wrong way. Basically all i need to do is pull users and their properties from Active Directory and put into SQL database tabels. And I've worked out how to do both separately, but I cant figure out how to put it all together.
If the CSV is just being used to cache the results, you could use a Dictionary to store the contents of the search results instead. Separating your code into functions could be helpful:
private static object GetFirstValue(ResultPropertyCollection properties,
string propertyName)
{
var propertyValues = properties[propertyName];
var result = propertyValues.Count == 0 ? null : propertyValues[0];
return result;
}
Then you could either use a dictionary to hold the property values, or you could create a type:
var results = new List<Dictionary<string, object>>();
foreach(SearchResult objResult in objSearchResults)
{
var properties = objResult.Properties;
var propertyDictionary = new Dictionary<string, object> {
{"FirstName", GetFirstValue(properties, "givenname")},
{"LastName", GetFirstValue(properties, "sn")},
{"UserName", GetFirstValue(properties, "samaccountname")},
};
results.Add(propertyDictionary);
}
Now you have a list of property bags.
This could also be a simple LINQ statement:
var results = objSearchResults.OfType<SearchResult>()
.Select(s => s.Properties)
.Select(p => new {
FirstName = (string)GetFirstValue(properties, "givenname"),
LastName = (string)GetFirstValue(properties, "sn"),
UserName = (string)GetValue(properties, "samaccountname"),
AccountExpires = GetDateTimeValue(properties, "accountexpires")
});
Use the dictionaries like this:
foreach(var item in results)
{
var command = new SqlCommand();
...
command.Parameters.AddWithValue("firstName", item["FirstName"]);
...
}

How to retrieve the values in a multi dimensional array using JSON?

I have created a multi dimensional array.. which stores the table rows values from database..I need to store the array elements into json format,and should display the array elements seperately(ie,row wise as in the data base table). my code is here..but it displays all the data in json format like this. :
{"question":"6"}{"question":"mother?"}{"question":"fghh"}{"question":"bbb"}{"question":"qwe"}{"question":"wer"}{"question":"5"}{"question":"colg?"}{"question":"sdf"}{"question":"scf"}{"question":"aaa"}{"question":"dfdf"}
my code is here :
SqlDataAdapter da = new SqlDataAdapter("select question_num,q_text,answer1_text,answer2_text,answer3_text,answer4_text from question_bank_details where question_num in (select top 2 question_num from question_bank_details order by newid())", con5);
DataTable dt = new DataTable();
da.Fill(dt);
int i; int j;
string[,] array_questions = new string[dt.Rows.Count, dt.Columns.Count];
for (i = 0; i < dt.Rows.Count; i++)
{
for (j = 0; j < dt.Columns.Count; j++)
{
array_questions[i, j] = dt.Rows[i][j].ToString();
//Response.Write(array_questions[i, j]);
}
}
foreach (string question_row in array_questions)
{
var ques = new { question = question_row };
JavaScriptSerializer js = new JavaScriptSerializer();
// string json = js.Serialize(question_row);
string json = js.Serialize(ques);
Response.ContentType = "application/json";
Response.Write(json);
}
}
how can i display it row wise as in the database table ? please help ?
I need the output like this :
{"question" : "6" "mother?" "bbb" "fghhh" "bbb" "qwe" "wer"}{"question" : "5" "colg" "sdf" "scf" "aaa" "ddd" "hjk"}
If you are wanting a JSON array of objects with named properties, try the following:
var rows = dt.Rows
.Cast<DataRow>()
.Select(row => new {
ID = row[0],
Question = row[1],
Answer1 = row[2],
Answer2 = row[3],
Answer3 = row[4],
Answer4 = row[5],
})
.ToArray();
string json = js.Serialize(rows);
If instead you want an array of arrays as JSON, try this:
dt.Rows
.Cast<DataRow>()
.Select(row => new string[] {
row[0].ToString(),
row[1].ToString(),
row[2].ToString(),
row[3].ToString(),
row[4].ToString(),
row[5].ToString(),
})
.ToArray();
string json = js.Serialize(rows);
If you want something else, please update your question accordingly.
Your serializing turns the data into JSON. What you are trying to do is not JSON.
According to the information you gave you are trying to do something which isn't standard - you'll probably need to code it yourself.

Categories