43
public string toJSON(SqlDataReader o)
{
    StringBuilder s = new StringBuilder();
    s.Append("[");
    if (o.HasRows)
        while (o.Read())
            s.Append("{" + '"' + "Id" + '"' + ":" + o["Id"] + ", "
            + '"' + "CN" + '"' + ":" + o["CatName"] + ", "
            + '"' + "Ord" + '"' + ":" + o["Ord"] + ","
            + '"' + "Icon" + '"' + ":" + o["Icon"] + "}, ");
    s.Remove(s.Length - 2, 2);
    s.Append("]");
    o.Close();
    return s.ToString();
}

I'm using here my own function to do serialization . I need to know if this is a good way or I should use another . BTW I've tried to use the JavaScriptSerializer but this didn't work with SqlDataReader . thanx

Rawhi
  • 6,155
  • 8
  • 36
  • 57

13 Answers13

74

If you want something that'll convert to arbitrary JSON, you could convert by serializing it into a Dictionary(Of string, object) thusly:

public IEnumerable<Dictionary<string, object>> Serialize(SqlDataReader reader)
{
    var results = new List<Dictionary<string, object>>();
    var cols = new List<string>();
    for (var i = 0; i < reader.FieldCount; i++) 
        cols.Add(reader.GetName(i));

    while (reader.Read()) 
        results.Add(SerializeRow(cols, reader));

    return results;
}
private Dictionary<string, object> SerializeRow(IEnumerable<string> cols, 
                                                SqlDataReader reader) {
    var result = new Dictionary<string, object>();
    foreach (var col in cols) 
        result.Add(col, reader[col]);
    return result;
}

And then use the NewtonSoft.Json JsonConvert object to get your JSON:

var r = Serialize(reader);
string json = JsonConvert.SerializeObject(r, Formatting.Indented);

UPDATE: If you just want to use built-in methods, and you happen to be using MVC, you can use the built in Json helper method on your newly serialized :

JsonResult Index(int id) {
    var r = Serialize(reader);
    return Json(r, JsonRequestBehavior.AllowGet);
}
Jonathan
  • 25,873
  • 13
  • 66
  • 85
  • 4
    This is the best solution for replacing old WebForms code that uses SqlDataSource and AutoGenerateColumns="True", which is what I need it for! You saved me 5 minutes of coding. Love the Internet. – John Zabroski Apr 28 '13 at 13:33
  • Glad you found it useful. That's what I used it for, too. :) – Jonathan Apr 29 '13 at 11:51
  • Honestly, this is so simple, you'd think NewtonSoft would have built it into their product, by now. – KWallace Jun 01 '22 at 22:07
28

This should do the job

private String sqlDatoToJson(SqlDataReader dataReader)
{
    var dataTable = new DataTable();
    dataTable.Load(dataReader);
    string JSONString = string.Empty;
    JSONString = JsonConvert.SerializeObject(dataTable);
    return JSONString;
}
YakovL
  • 7,557
  • 12
  • 62
  • 102
gerzalez
  • 317
  • 3
  • 2
25

I encounter use cases where the number of rows being returned by the data reader may become problematic with respect to memory consumption. The following code uses a JsonWriter (from JSON.NET) over a stream. One can certainly debate the utility of enormous JSON documents, but sometimes our use cases are dictated by others :-)

A few notes:

  • My SqlDataReader may contain multiple result sets ('tables')
  • I may be sending the output to a FileStream or an HttpResponse stream
  • I've 'abstracted' my object names to match the first column returned per result set
  • Because of the potential for large result sets, I use async methods of the SqlDataReader.
  • I'm letting JSON.NET handle all the serialization issue of the actual data contained in the data reader results.

The code:

var stream = ... // In my case, a FileStream or HttpResponse stream
using (var writer = new JsonTextWriter(new StreamWriter(stream)))
{
    writer.WriteStartObject();  
    do
    {
        int row = 0;
        string firstColumn = null;
        while (await reader.ReadAsync())
        {
            if (row++ == 0)
            {
                firstColumn = reader.GetName(0);
                writer.WritePropertyName(string.Format("{0}Collection", firstColumn));
                writer.WriteStartArray();   
            }
            writer.WriteStartObject();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                if (!reader.IsDBNull(i)) { 
                    writer.WritePropertyName(reader.GetName(i));
                    writer.WriteValue(reader.GetValue(i));
                }
            }
            writer.WriteEndObject(); 
        }
        writer.WriteEndArray();
    } while (await reader.NextResultAsync());

    writer.WriteEndObject();
}

An example of heterogeneous output would be:

{
    "ContactCollection": {
        "ContactItem": [{
                "ContactID": "1",
                "Contact": "Testing",
            },
            {
                "ContactID": "2",
                "Contact": "Smith, John",
            },
            {
                "ContactID": "4",
                "Contact": "Smith, Jane",
            }
        ],
        "MessageItem": [{
                "MessageID": "56563",
                "Message": "Contract Review Changed",
            },
            {
                "MessageID": "56564",
                "Message": " Changed",
            },
            {
                "MessageID": "56565",
                "Message": "Contract Review - Estimated Completion Added.",
            }
        ]
    }
}

Reference:

Eric Patrick
  • 2,097
  • 2
  • 20
  • 31
17

Another option would be to use James Newton-King's excellent JSON.NET library - http://www.newtonsoft.com/json

Here's a quick example on how to use it to build up a collection and then output it as a JSON-serialized string:

using Newtonsoft.Json;

class Program
{
    static void Main(string[] args)
    {
        ArrayList objs = new ArrayList();

        //get the data reader, etc.
        while(o.Read())
        {
            objs.Add(new
            {
                Id = o["Id"],
                CN = o["CatName"],
                Ord = o["Ord"],
                Icon = o["Icon"]
            });
        }

        //clean up datareader

        Console.WriteLine(JsonConvert.SerializeObject(objs));
        Console.ReadLine();
    }
}

You could do the same with your looping by reading in each row of your SqlDataReader into an anonymous object and then use JSON.NET to serialize it to a string.

Hope this helps!

dbc
  • 104,963
  • 20
  • 228
  • 340
David Hoerster
  • 28,421
  • 8
  • 67
  • 102
10

Since SQL Server 2016, Microsoft embedded this feature with sql queries. You can achieve it by using FOR JSON keyword at the end of your queries.

select * from table_example where somecolumn = somecondition FOR JSON AUTO

for more details and example you can go through this official documents Format JSON Output Automatically with AUTO Mode (SQL Server)

Here is the C# code example from Microsoft to get JSON string from SQL queries.

var queryWithForJson = "SELECT ... FOR JSON";
var conn = new SqlConnection("<connection string>");
var cmd = new SqlCommand(queryWithForJson, conn);
conn.Open();
var jsonResult = new StringBuilder();
var reader = cmd.ExecuteReader();
if (!reader.HasRows)
{
    jsonResult.Append("[]");
}
else
{
    while (reader.Read())
    {
        jsonResult.Append(reader.GetValue(0).ToString());
    }
}

Warning: This solution is only valid for SQL SERVER 2016 and higher.

Muhammad Saqib
  • 2,185
  • 3
  • 35
  • 48
8

I use this code, based on Jonathan's answer:

private IEnumerable<Dictionary<string, object>> ConvertToDictionary(IDataReader reader)
{
    var columns = new List<string>();
    var rows = new List<Dictionary<string, object>>();

    for (var i = 0; i < reader.FieldCount; i++)
    {
        columns.Add(reader.GetName(i));
    }

    while (reader.Read())
    {
        rows.Add(columns.ToDictionary(column => column, column => reader[column]));
    }

    return rows;
}

And then:

var rows = this.ConvertToDictionary(reader);

return JsonConvert.SerializeObject(rows, Formatting.Indented);
Community
  • 1
  • 1
Lenin
  • 679
  • 2
  • 10
  • 15
8

Try this:

o = cmd.ExecuteReader();
var dataQuery = from d in o.Cast<DbDataRecord>()
                select new
                {
                    Id = (String)d["Id"],
                    CN = (String)d["CatName"],
                    Ord = (String)d["Ord"],
                    Icon = (String)d["Icon"]
                };
var data = dataQuery.ToArray();
JavaScriptSerializer serializer = new JavaScriptSerializer();
String jsonData = serializer.Serialize(data);
Jenna Leaf
  • 2,255
  • 21
  • 29
Chandu
  • 81,493
  • 19
  • 133
  • 134
  • 1
    I must have missed the MVC tag on the question. Oh, wait a sec, I didn't, since it isn't there... – Oded Feb 22 '11 at 20:50
  • Yes using LINQ but this is also slower than this function . right !! – Rawhi Feb 22 '11 at 20:50
  • @Oded: Was deeply obsessed with MVC lately :). Changed the post to remove and MVC falvor in the answer if tht makes u happy – Chandu Feb 22 '11 at 20:53
  • @Cybernate - I just wasn't sure that `return Json(...)` would have even compiled for the OP ;) – Oded Feb 22 '11 at 20:55
  • @Oded: Thanks for your observation though.. maybe the user is not using it in MVC context. – Chandu Feb 22 '11 at 20:57
  • @Rawhi: When you say "also slower than this function" what does this function refer to? – Chandu Feb 22 '11 at 21:55
  • @Rahwi: I haven't benchmarked the function, however I think both the versions perform almost the same. The one with Linq looks more readable and maintainable to me.... – Chandu Feb 23 '11 at 13:09
  • Chandu: this is great using Linq! I have added another answer that employs Method Syntax as well. It is tested. Check it out! Also because I was doing old school WCF, I just used the DataContractSerializer to create JSON string. – Jenna Leaf Jun 17 '16 at 19:14
5

With Cinchoo ETL - an open source library, you can export SqlDataReader to JSON easily with few lines of code

string connectionstring = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Northwind;Integrated Security=True";
StringBuilder sb = new StringBuilder();

using (var conn = new SqlConnection(connectionstring))
{
    conn.Open();
    var comm = new SqlCommand("SELECT top 2 * FROM Customers", conn);

    using (var parser = new ChoJSONWriter(sb))
        parser.Write(comm.ExecuteReader());
}

Console.WriteLine(sb.ToString());

Output:

[
 {
  "CustomerID": "ALFKI",
  "CompanyName": "Alfreds Futterkiste",
  "ContactName": "Maria Anders",
  "ContactTitle": "Sales Representative",
  "Address": "Obere Str. 57",
  "City": "Berlin",
  "Region": {},
  "PostalCode": "12209",
  "Country": "Germany",
  "Phone": "030-0074321",
  "Fax": "030-0076545"
 },
 {
  "CustomerID": "ANATR",
  "CompanyName": "Ana Trujillo Emparedados y helados",
  "ContactName": "Ana Trujillo",
  "ContactTitle": "Owner",
  "Address": "Avda. de la Constitución 2222",
  "City": "México D.F.",
  "Region": {},
  "PostalCode": "05021",
  "Country": "Mexico",
  "Phone": "(5) 555-4729",
  "Fax": "(5) 555-3745"
 }
]
Cinchoo
  • 6,088
  • 2
  • 19
  • 34
2

This is to enhance Chandu's Linq answer that uses query syntax (from ... select ...). If you prefer Method Syntax, here is your answer.

drdr = cmd.ExecuteReader();
Record[] recs = drdr.Cast<DbDataRecord>().Select( data=>new Record{
            GraphID=(drdr.IsDBNull(0) ? "" : (string)data["LabelX"])
        , XAxis=(drdr.IsDBNull(1) ? "1999-09-09 00:00:00" : Convert.ToDateTime(data["XDate"]).ToString("yyyy-MM-dd HH:mm:ss"))
        , YVal=(drdr.IsDBNull(2) ? 0 : int.Parse(data["YFreq"].ToString()))
        }).ToArray();

MemoryStream mem = new MemoryStream();
DataContractJsonSerializer szr = new DataContractJsonSerializer(typeof(Record[]));
szr.WriteObject(mem, recs);
String jsonData = Encoding.UTF8.GetString(mem.ToArray(), 0, (int)mem.Length); 

Hope it helps somebody.

Jenna Leaf
  • 2,255
  • 21
  • 29
1

This can't be that hard. This is what I've done when I want to return search results to a web page as JSON.

First, have a class like this

public class SearchResult
{
    public string model_no { get; set; }
    public string result_text { get; set; }
    public string url { get; set; }
    public string image_url { get; set; }
}

and then have the code below.

        string sql_text = "select * from product_master where model_no like @search_string and active=1";
        SqlConnection connection = new SqlConnection(sql_constr);
        SqlCommand cmd = new SqlCommand(sql_text, connection);
        cmd.Parameters.AddWithValue("@search_string", "%" + search_string + "%");
        connection.Open();

        SqlDataReader rdr = cmd.ExecuteReader();

        List<SearchResult> searchresults = new List<SearchResult>();

        while (rdr.Read())
        {
            SearchResult sr = new SearchResult();
            sr.model_no = rdr["model_no"].ToString();
            sr.result_text = rdr["product_name"].ToString();
            sr.url = rdr["url_key"].ToString();

            searchresults.Add(sr);

        }
        connection.Close();

        //build json result
        return Json(searchresults, JsonRequestBehavior.AllowGet);

this works for me very well..

BenW
  • 1,393
  • 1
  • 16
  • 26
1

Further to Jonathan's Answer, I had a similar requirement in ASP.NET Core to convert the result of an SQLDataReader to a JSON string or a Result Object, so I created an extension method for it as :

 public static class MyExtensions
    {
        public async static Task<string> toJSON(this SqlDataReader reader)
        {            
            var results = await reader.GetSerialized();
            return JsonConvert.SerializeObject(results, Formatting.Indented);
        }
        public async static Task<IEnumerable<Dictionary<string, object>>> GetSerialized(this SqlDataReader reader)
        {
            var results = new List<Dictionary<string, object>>();
            var cols = new List<string>();
            for (var i = 0; i < reader.FieldCount; i++)
                cols.Add(reader.GetName(i));

            while (await reader.ReadAsync())
                results.Add(SerializeRow(cols, reader));

            return results;
        }
        private static Dictionary<string, object> SerializeRow(IEnumerable<string> cols,
                                                        SqlDataReader reader)
        {
            var result = new Dictionary<string, object>();
            foreach (var col in cols)
                result.Add(col, reader[col]);
            return result;
        }
    }

& used it as per my requirement as :

var result = await reader.GetSerialized(); //to get the result object

or

string strResult = await reader.toJSON(); //to get the result string

I created an async method because I had some other things to be done till the reading was finished from database.

Community
  • 1
  • 1
Abdul Rehman Sayed
  • 6,532
  • 7
  • 45
  • 74
0

I made the following method where it converts any DataReader to JSON, but only for single depth serialization:

you should pass the reader, and the column names as a string array, for example:

String [] columns = {"CustomerID", "CustomerName", "CustomerDOB"};

then call the method

public static String json_encode(IDataReader reader, String[] columns)
    {
        int length = columns.Length;

        String res = "{";

        while (reader.Read())
        {
            res += "{";

            for (int i = 0; i < length; i++)
            {
                res += "\"" + columns[i] + "\":\"" + reader[columns[i]].ToString() + "\"";

                if (i < length - 1)
                    res += ",";
            }

            res += "}";
        }

        res += "}";

        return res;
    }
Amjad Abu Saa
  • 1,644
  • 2
  • 15
  • 11
  • 1
    In c# strings are immutable, so your loop based concatenation could potentially require a lot of memory allocation. I recommend implementing a string builder if you're going to stick with the approach you have outlined. Having said that, I personally wouldn't try to write a JSON encoder like this. For example, what happens to the validity of your output if the content of a column name or a reader column value contains a quote, comma, or a bracket? – Bennett Dill Oct 14 '13 at 18:47
-1

add reference : System.Web.Extensions to project then

using System.Web.Script.Serialization;

in c# code, you can use write :

 var json = new JavaScriptSerializer().Serialize(obj);
Abdul Rehman Sayed
  • 6,532
  • 7
  • 45
  • 74