8:31 0 0
Parse JSON DataTable

Parse JSON DataTable

  DrUalcman |  febrero 72021

Hoy voy a comenzar con algo que tengo pendiente desde hace mucho, pero que mucho tiempo, y es ir comentando una serie de utilidades que a lo largo de mi vida como programador he ido desarrollando y, que seguro que hay mucho por ahi que hace lo mismo, pero yo lo he englobado todo dentro de una librería que he llamdado DrUalcman Library y no es ni más ni menos que la recopilación de todos estos trucos, o utilidades a la hora de programar. Hoy comenzaré con una de las que más utilizo.

El github lo puedes encontrar en este enlace, donde puedes descargar toda la librería, no sólo esta utilidad que te describo y presento hoy.

Pasar JSON a un DataTable

Sin duda muchos habran utilizado NewtonSoft para convertir desde un JSON a un objeto, o lista de objetos, incluso, cuando no tienes una clase específicamente creada y lo pasar a un Objeto Dinámico. Pero desde mi punto de vista, tener que cargar TODO lo que es la librería NewtonSoft para sólo poder acceder a los datos de un JSON desde C# cuando se recibe desde un JAVASCRIP, pues siempre se me hace muy pesado, además que algunas veces se me hace hasta engorroso y dificil de usar. Es por eso que tras varios años he ido perfeccionando esta función y sus derivados, para poder pasar desde un JSON a un DataTable sin más. Luego ya se acceder a los datos como cualquier DataTable desde código.

Forma de uso

El método es muy sencillo de usar, sólo tienes que pasar un JSON y te devuelve un DataTable. Una pequeña pega es que si el JSON es complejo, osea, que tiene un objeto JSON con otro JSON anidado, entonces esta funcion te va a incluir TODO esos datos dentro de una clumna con el nombre de la propiedad que tenia ese otro JSON.

DataTable dt = drualmcan.dataBases.JsonStringToDataTable(jsonStringData);


Como se ha conseguido

Empecemos explicando un poco la funcion. 

1. Eliminaremos los posibles caracteres que indican que es un arrary [ y ].

2. Luego despedazamos el array de elementos en un string[] buscando los caracteres de cierre de e inicio de elmenentos },{.

3. Recorremos el resultado para nuevamente despedazar en columnas los nombre de cada campo.

4. Eliminamos los caracteres de inicio y finalizacion de elemento { y }.

5. Despedazamos en un string[] de nuevo, pero ahora por el caracter , que separa los campos de cada columna.

6. Recorremos el resultado para localizar los nombre de los campos, situados a la izquierda del caracter :, y los agregamos a una list de string. Si se encuentra mas de uno agregamos el numero del indice para evitar duplicados en las columnas.

7. Cuando tenemos todos los nombre de los campos, eecorremos las lista de string para agregar los nombre de columna a nuestro objero DataTable

8. Repetimos el proceso de recorer los datos del string[] con los datos del JSON para poder recuperar los datos de las lineas de nuestro DataTable. Pero esta vez obteniendo el contenido del string a la derecha del caracter :. En cada iteracion crearemos un objeto DataRow desde el DataTable con las columnas y sus nombre que definimos previamente, Y agregamos el resultado dentro de la columna con el nombre correspondiente. Como los nombre de columna estan en un array, siempre tendra la misma posicion dentro del string[] que hemos creado, por lo que es facil identificar que columna estamos agregando los datos.

Aqui el script completo

    public static DataTable JsonStringToDataTable(string jsonString)
{
DataTable dt = new DataTable();
if (!string.IsNullOrEmpty(jsonString) && jsonString.ToLower() != "undefined")
{
jsonString = jsonString.Replace("}, {", "},{");
jsonString = CheckComa(jsonString);
string[] jsonStringArray = System.Text.RegularExpressions.Regex.Split(jsonString.Replace("[", "").Replace("]", ""), "},{");
List ColumnsName = new List();
foreach (string jSA in jsonStringArray)
{
string[] jsonStringData = System.Text.RegularExpressions.Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
foreach (string ColumnsNameData in jsonStringData)
{
try
{
int idx = ColumnsNameData.IndexOf(":");
string ColumnsNameString = ColumnsNameData.Substring(0, idx - 1).Replace(""", "").Trim();
if (!ColumnsName.Contains(ColumnsNameString))
{
ColumnsName.Add(ColumnsNameString);
}
else
{
//if found more than one column with same name add the id to difference the column name
ColumnsName.Add(ColumnsNameString + (ColumnsName.Count() - 1).ToString());
}

try
{
string RowColumns = rowData.Substring(0, idx - 1).Replace(""", "").Trim();
string RowDataString = rowData.Substring(idx + 1).Replace(""", "").Trim();
nr[columnNumber] = RowDataString; //because the columns always come in same order use the index not the name
columnNumber++;
}
catch
{
continue;
}
}
catch
{
throw new Exception(string.Format("Error Parsing Column Name : {0}", ColumnsNameData));
}
}
break;
}
foreach (string AddColumnName in ColumnsName)
{
dt.Columns.Add(AddColumnName);
}
foreach (string jSA in jsonStringArray)
{
string[] RowData = System.Text.RegularExpressions.Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
DataRow nr = dt.NewRow();
int columnNumber = 0; //reset index of the column per each element
foreach (string rowData in RowData)
{
try
{
int idx = rowData.IndexOf(":");
string RowDataString = rowData.Substring(idx + 1).Replace(""", "").Trim();
nr[columnNumber] = RowDataString; //because the columns always come in same order use the index not the name
columnNumber++;
}
catch
{
continue;
}
}
dt.Rows.Add(nr);
}
}
return dt;
}

Ayudas utilizadas.

Con el tiempo, y tras utilizar mucho esta funcion, se fue mejorando un poco, y una de las ayudas utilizadas es CheckComa(jsonString). Esta funcion lo que hace es buscar una coma dentro de los caracteres de inicio y fin de la fila ("). Ya que nuestro separador de campos es precisamente la coma (,) y lo elimina.

Por desgracia, esto genera que realmente se pierdan comas en el proceso. Pero aqui os aliento a mejorar esta funcion y asi poder mantener esas comas.

Una de las maneras que se me ocurre, solo por comentar, es utilizar REGEX para hacer esta primera separacion buscando }, o con un espacio al final. Pero ahi lo dejo para que lo investiguen ustedes. Ya qye en las filas no suele tener }, al final. Ahi esta el problema.

            /// 
/// Check don't have a , between " on the text.
///

///
///
private static string CheckComa(string text)
{
string retorno = string.Empty;
string caracter;
string siguiente;
for (int i = 0; i < text.Length; i++)
{
caracter = text.Substring(i, 1);
int n = i + 1;
if (n < text.Length)
{
siguiente = text.Substring(n, 1);
if (caracter == "," && siguiente != """)
{
if (caracter == "," && siguiente != "{") retorno += string.Empty;
else if (caracter == "," && siguiente == " ")
{
n++;
siguiente = text.Substring(n, 1);
if (caracter == "," && siguiente != """)
{
if (caracter == "," && siguiente != "{") retorno += string.Empty;
else retorno += caracter;
}
else retorno += caracter;
}
else retorno += caracter;
}
else retorno += caracter;
}
else retorno += caracter;
}
return retorno;
}

Nueva idea mientras escribia esta blog

Mientras estaba escribiendo este blog, se me ocurrio una nueva idea de conversion. El otro dia comentando con otros programadores esta funcion, me decian que un DataTable era muy pesado, y que trabajar con listas es mejor opcion. Entonces, se me ha ocurrido realizar lo mismo, pero ahora insertando los datos en un lista con un diccionario como elemento. Por lo que ahora vamos a pasarlo a List>. Esto aun no lo he probado. Otra idea que tengo es crear un objeto dinamico y pasarlo a una lista de ese objeto. Pero eso ya para otro blog. Aqui va el codigo, ya me comentareis si funciona o si se os ocurre otra forma de hacerlo

public static List> JsonStringToListDictionary(string jsonString)
{
List> dt = new List>();
if (!string.IsNullOrEmpty(jsonString) && jsonString.ToLower() != "undefined")
{
jsonString = jsonString.Replace("}, {", "},{");
jsonString = CheckComa(jsonString);
string[] jsonStringArray = System.Text.RegularExpressions.Regex.Split(jsonString.Replace("[", "").Replace("]", ""), "},{");
List ColumnsName = new List();
foreach (string jSA in jsonStringArray)
{
string[] jsonStringData = System.Text.RegularExpressions.Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
foreach (string rowData in jsonStringData)
{
try
{
int idx = rowData.IndexOf(":");
string ColumnsNameString = rowData.Substring(0, idx - 1).Replace("\"", "").Trim();
if (!ColumnsName.Contains(ColumnsNameString))
{
ColumnsName.Add(ColumnsNameString);
}
else
{
//if found more than one column with same name add the id to difference the column name
ColumnsName.Add(ColumnsNameString = ColumnsNameString + (ColumnsName.Count() - 1).ToString());
}
}
catch
{
throw new Exception(string.Format("Error Parsing Column Name : {0}", rowData));
}
}
break;
}
foreach (string jSA in jsonStringArray)
{

string[] RowData = System.Text.RegularExpressions.Regex.Split(jSA.Replace("{", "").Replace("}", ""), ",");
int columnNumber = 0; //reset index of the column per each element
Dictionary valuePairs = new Dictionary();
foreach (string rowData in RowData)
{
try
{
int idx = rowData.IndexOf(":");
string RowDataString = rowData.Substring(idx + 1).Replace("\"", "").Trim();
valuePairs.Add(ColumnsName[columnNumber], RowDataString);
columnNumber++;
}
catch
{
continue;
}
}
dt.Add(valuePairs);
}
}
return dt;
}

Hapy codding


#JSON #CSHARP


0 Comentarios

 
 
 

Archivo