2009-01-16 24 views
19

Recientemente necesité serializar una tabla de datos a JSON. Donde estoy todavía estamos en .Net 2.0, así que no puedo usar el serializador JSON en .Net 3.5. Pensé que esto debe haberse hecho antes, así que fui buscando en línea y found a number de differentoptions. Algunos de ellos dependen de una biblioteca adicional, que me resultaría difícil adelantar aquí. Otros requieren la primera conversión a List<Dictionary<>>, que parecía un poco incómodo e innecesario. Otro trató todos los valores como una cadena. Por una razón u otra, no podía realmente estar detrás de ninguno de ellos, así que decidí lanzar el mío, que se publica a continuación.DataTable a JSON

Como puede ver al leer los comentarios //TODO, está incompleto en algunos lugares. Este código ya está en producción aquí, por lo que "funciona" en el sentido básico. Los lugares donde está incompleto son los lugares donde sabemos que nuestros datos de producción no lo afectarán (sin timespans o arrays de bytes en el db). La razón por la que estoy publicando aquí es porque siento que esto puede ser un poco mejor, y me gustaría ayudar a terminar y mejorar este código. Cualquier entrada de bienvenida.

Tenga en cuenta que esta capacidad está integrada en .Net 3.5 y posterior, por lo que la única razón para utilizar este código hoy es si todavía está limitado a .Net 2.0. Incluso entonces, JSON.Net se ha convertido en la biblioteca goto para este tipo de cosas.

public static class JSONHelper 
{ 
    public static string FromDataTable(DataTable dt) 
    { 
     string rowDelimiter = ""; 

     StringBuilder result = new StringBuilder("["); 
     foreach (DataRow row in dt.Rows) 
     { 
      result.Append(rowDelimiter); 
      result.Append(FromDataRow(row)); 
      rowDelimiter = ","; 
     } 
     result.Append("]"); 

     return result.ToString(); 
    } 

    public static string FromDataRow(DataRow row) 
    { 
     DataColumnCollection cols = row.Table.Columns; 
     string colDelimiter = ""; 

     StringBuilder result = new StringBuilder("{");  
     for (int i = 0; i < cols.Count; i++) 
     { // use index rather than foreach, so we can use the index for both the row and cols collection 
      result.Append(colDelimiter).Append("\"") 
        .Append(cols[i].ColumnName).Append("\":") 
        .Append(JSONValueFromDataRowObject(row[i], cols[i].DataType)); 

      colDelimiter = ","; 
     } 
     result.Append("}"); 
     return result.ToString(); 
    } 

    // possible types: 
    // http://msdn.microsoft.com/en-us/library/system.data.datacolumn.datatype(VS.80).aspx 
    private static Type[] numeric = new Type[] {typeof(byte), typeof(decimal), typeof(double), 
            typeof(Int16), typeof(Int32), typeof(SByte), typeof(Single), 
            typeof(UInt16), typeof(UInt32), typeof(UInt64)}; 

    // I don't want to rebuild this value for every date cell in the table 
    private static long EpochTicks = new DateTime(1970, 1, 1).Ticks; 

    private static string JSONValueFromDataRowObject(object value, Type DataType) 
    { 
     // null 
     if (value == DBNull.Value) return "null"; 

     // numeric 
     if (Array.IndexOf(numeric, DataType) > -1) 
      return value.ToString(); // TODO: eventually want to use a stricter format. Specifically: separate integral types from floating types and use the "R" (round-trip) format specifier 

     // boolean 
     if (DataType == typeof(bool)) 
      return ((bool)value) ? "true" : "false"; 

     // date -- see http://weblogs.asp.net/bleroy/archive/2008/01/18/dates-and-json.aspx 
     if (DataType == typeof(DateTime))  
      return "\"\\/Date(" + new TimeSpan(((DateTime)value).ToUniversalTime().Ticks - EpochTicks).TotalMilliseconds.ToString() + ")\\/\""; 

     // TODO: add Timespan support 
     // TODO: add Byte[] support 

     //TODO: this would be _much_ faster with a state machine 
     //TODO: way to select between double or single quote literal encoding 
     //TODO: account for database strings that may have single \r or \n line breaks 
     // string/char 
     return "\"" + value.ToString().Replace(@"\", @"\\").Replace(Environment.NewLine, @"\n").Replace("\"", @"\""") + "\""; 
    } 
} 

Actualización:
Esto es viejo ahora, pero quería señalar algo acerca de cómo este código controla fechas. El formato que utilicé tenía sentido en ese momento, por la razón exacta en la url. Sin embargo, esa lógica incluye lo siguiente:

para ser perfectamente honesto, JSON esquema no resuelve el problema por lo que es posible "subtipo" una cadena como un literal de fecha, pero esto todavía es un trabajo en progreso y lo hará Tómese el tiempo antes de que se alcance una adopción significativa.

Bueno, el tiempo ha pasado. Hoy, está bien solo usar el formato de fecha ISO 8601. No voy a molestarme en cambiar el código, porque realmente: esto es antiguo. Solo usa JSON.Net.

+0

Si esto se pidiera hoy, debería estar en el intercambio de pila de revisión de código. Lo he marcado para que se mueva allí, pero la única razón por la que me llamó la atención es que acaba de obtener una insignia de oro de "Pregunta famosa". Sería una pena perder ese recuento de visitas en la migración. –

+0

Creo que esto sería demasiado viejo para migrar. ¿Puede vivir aquí si estás de acuerdo con eso? – Kev

+0

No me importa de ninguna manera, solo estoy pensando en la mejor opción. He tenido otras cosas viejas movidas. –

Respuesta

1

Encontré esto: http://www.bramstein.com/projects/xsltjson/ Puede convertir su datatable a xml y usar una hoja de estilo xslt para convertir el xml a json.

Es más una solución que una solución real.

+1

Solo un aviso: no sé cómo se votó negativamente, pero no tengo el hábito de rechazar las respuestas de buena fe a mis preguntas. –