How to Convert Microsoft ADOMD Data Source to JSON
Introduction
Hi all, I hope you are all fine. This article helps you to understand how to convert your ADOMD data source into the JavaScript Object Notation (JSON) format. If you are new to ADOMD, please read here.
As I said, we will convert the source to JSON, so you must understand what JSON is and its importance. Am I right?
Important of JSON
JSON stands for JavaScript Object Notation (basically JSON itself is JavaScript). It is a data format that we can format and analyze using JavaScript. It is easy to use. You can get more here.
Background
For the past few months I am working on ADOMD data sources. When you have become experienced in SQL, you may encounter some difficulties to play with sources. As far as my experience is concerned, playing with the data is not as easy as in SQL in ADOMD. The ADOMD data source may be an Excel file. In my case what exactly the client is doing was uploading the Excel files that they have created, to the DB.
Getting back to the point, when you have a client-side grid that accepts data in only JSON format in your ADOMD application, what will you do? I have searched for a solution to convert the source to the JSON for many days. But I could not find a solution. So I came up with the idea of converting the ADOMD data source (Cell Set) to a HTML table and in the client-side convert that to JSON. You can read that article here.
Now that was in the initial stage, where I was new to ADOMD. We developers never stop Googling, right? To be frank I am passionate about R&D work (in which we may need to search more and more). Now I have discovered some more methods to convert the source to JSON. I thought to share that information, so that someone may determine it is useful.
I am working in MVC, and I am using the below namespace for the process.
The Process
In this I am not taking the ADOMD data source as a Cell Set that is popularly used in such applications. Here I will explain two methods or ways to do the process.
In the first part we will use an adapter and fill the data into the data table and convert that to the JSON in the server-side itself. The problem in this method is, it needs many loops for formatting and creating.
In the second part we will use a data reader, while the object reads we will do the formatting and creating the JSON that needs only one loop. Sounds cool, right?
Using the code
Let us explain the first method.
When you use an adapter and fill in the data table, the problem here you encounter is, the data table column name would be different and it will contain the hierarchy of the cube cells.
For example: The data table header contains “.[MEMBER_CAPTION]”
So here I am just determining the actual header name from all the header columns. I am using a loop for that.
Please note that it is completely based on my requirements. You may want to do a different process of formatting.
To format I am using the following function.
[csharp]
public DataTable HiMapColumnExcuteQuery(string query, string adoMDConnection)
{
string readerString = string.Empty;
try
{
using (AdomdConnection conn = new AdomdConnection(adoMDConnection))
{
conn.Open();
using (AdomdCommand cmd = new AdomdCommand(query, conn))
{
DataTable dt = new DataTable();
AdomdDataAdapter da = new AdomdDataAdapter(cmd);
da.Fill(dt);
List<string> curColumn = new List<string>();
string col = string.Empty;
if (dt.Rows.Count > 0 && dt.Columns.Count > 0)
{
for (int i = 0; i < dt.Columns.Count; i++)
{
string columnName = dt.Columns[i].ColumnName.Replace(“.[MEMBER_CAPTION]”, “”).Trim();
curColumn = columnName.Split(new string[] { “.” }, StringSplitOptions.None).ToList();
col = curColumn[curColumn.Count – 1].Replace(“[“, “”).Replace(“]”, “”);
if (Convert.ToString(col.Trim()).ToLower() == “latitude”)
col = “lat”;
if (Convert.ToString(col.Trim()).ToLower() == “longitude”)
col = “lon”;
dt.Columns[i].ColumnName = col;
}
dt.AcceptChanges();
}
return dt;
}
}
}
catch (Exception)
{
throw;
}
finally
{
}
}
[/csharp]
The function expects two parameters, one is the query that you need to execute and other is the connection. Here I am using this source for the Hi maps, as you all know it is important to provide the lat and lon for loading the map. So I am doing such formatting here.
Once the formatting is done, I can convert the data to the JSON foramt. For that I am using another function. You can see the function below.
[csharp]
public string GetJsonWithZeroForNull(DataTable dt)
{
try
{
if (dt == null)
{
throw new ArgumentNullException(“dt”);
}
System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
serializer.MaxJsonLength = int.MaxValue;
List<Dictionary<string, object>> rows =
new List<Dictionary<string, object>>();
Dictionary<string, object> row = null;
foreach (DataRow dr in dt.Rows)
{
row = new Dictionary<string, object>();
foreach (DataColumn col in dt.Columns)
{
if (dr[col] == null || Convert.ToString(dr[col]).ToLower() == “undefined” || Convert.ToString(dr[col]).ToLower() == “unknown”)
continue;
else
row.Add(col.ColumnName.Trim(), dr[col]);
}
rows.Add(row);
}
return serializer.Serialize(rows);
}
catch (Exception)
{
throw;
}
}
[/csharp]
Here I am omitting the data that all are not valid. At the end it will just serialize our rows and return the data in the format of JSON.
You can determine that if the data is greater then looping through the data twice may impact the performance.
And also we all know that a data reader is much better than a data adapter. If you do not understand why, please read here.
Now we will move to the next part. Please see the following function for doing that.
[csharp]
public string createJsonFromDataReader(string query, string adoMDConnection)
{
string readerString = string.Empty;
try
{
List<string> curColumn = new List<string>();
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
string columnName = string.Empty;
string fieldVal = string.Empty;
string prevFieldVal = string.Empty;
AdomdDataReader rdr;
using (AdomdConnection conn = new AdomdConnection(adoMDConnection))
{
conn.Open();
using (AdomdCommand cmd = new AdomdCommand(query, conn))
{
//cmd.Properties.Add(“ReturnCellProperties”, true);
rdr = cmd.ExecuteReader();
if (rdr != null)
{
using (JsonWriter myJson = new JsonTextWriter(sw))
{
myJson.WriteStartArray();
while (rdr.Read())
{
myJson.WriteStartObject();
int fields = rdr.FieldCount;
for (int i = 0; i < fields; i++)
{
if (rdr[i] != null )
{
fieldVal = rdr[i].ToString();
if (i != 0 && rdr[i-1] != null)
prevFieldVal = rdr[i – 1].ToString();
else
prevFieldVal = “First”;
if ((fieldVal == null || fieldVal.ToLower().Trim() == “undefined” || fieldVal.ToLower().Trim() == “unknown”)
&& (prevFieldVal == null || prevFieldVal.ToLower().Trim() == “undefined” || prevFieldVal.ToLower().Trim() == “unknown”))
{
continue;
}
else
{
columnName = rdr.GetName(i).Replace(“.[MEMBER_CAPTION]”, “”).Trim();
curColumn = columnName.Split(new string[] { “.” }, StringSplitOptions.None).ToList();
columnName = curColumn[curColumn.Count – 1].Replace(“[“, “”).Replace(“]”, “”);
if (Convert.ToString(columnName.Trim()).ToLower() == “latitude”)
columnName = “lat”;
if (Convert.ToString(columnName.Trim()).ToLower() == “longitude”)
columnName = “lon”;
myJson.WritePropertyName(columnName);
myJson.WriteValue(rdr[i]);
}
}
}
myJson.WriteEndObject();
}
myJson.WriteEndArray();
}
}
else
{
return “No Records to display”;
}
}
}
return sw.ToString();
}
catch (Exception)
{
throw;
}
finally
{
}
}
[/csharp]
Please understand that I have created an object (myJson) for the class JsonWriter. Our complete process is based on this object. Again the complete logic and formatting conditions are based on my requirements.
Here we are using built-in functions of the JsonWriter class. Let us list them.
You can see that I am doing both the formatting and creating of the JSON with one loop that will improve the performance.
In the process we are appending the values to the string builder and finally the function will return the JSON in the required format.
You have done it. Great.
Please use StringBuilder in your applications instead of string. Use a string variable only if it is necessary. Using string variables will create separate memory allocations whenever you assign values to it. Please read more here.
Note: I have implemented this for loading high maps. That’s why I have formatted it in this way. Please use your own formatting depending on your requirements.
Point of interest
ADOMD, ADOMD data source to JSON, ADOMD Cell Set to JSON, ADOMD data adapter to JSON, ADOMD data reader to JSON.
Conclusion
Please do not forget to give your valuable suggestions.
That is all for the day, will see you in another article.
Kindest Regards
Sibeesh Venu