2012-04-19 4 views
8

Domanda: sto esportando un System.Data.DataTable in XML. Finora funziona bene. Ma voglio avere tutti i dati in attributi, che funziona bene pure. Ma il mio problema ora, se in una colonna, tutte le righe sono NULL, non vengono scritti attributi vuoti. Quindi, se leggo l'XML su un DataTable, manca questa colonna ...Come esportare un DataTable in Xml con TUTTE le colonne come attributi?

Come posso forzare a scrivere tutte le colonne anche quando sono vuote?
(tipo di dati non stringa necessarely)

public void ExportTable(string strDirectory, DataTable dtt) 
{ 
    using (System.Data.DataSet ds = new System.Data.DataSet()) { 
     string strTable = dtt.TableName; 

     ds.Tables.Add(dtt); 
     ds.DataSetName = strTable; 

     // Move data to attributes 
     foreach (DataTable dt in ds.Tables) { 

      foreach (DataColumn dc in dt.Columns) { 
       dc.ColumnMapping = MappingType.Attribute; 
      } 

     } 

     System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings(); 
     settings.Indent = true; 
     //settings.Encoding = System.Text.Encoding.GetEncoding("ISO-8859-1") 
     settings.Encoding = System.Text.Encoding.UTF8; 
     settings.CloseOutput = true; 
     settings.CheckCharacters = true; 
     settings.NewLineChars = "\r\n"; 
     // vbCr & vbLf 

     // Write as UTF-8 with indentation 
     using (System.Xml.XmlWriter w = System.Xml.XmlWriter.Create(System.IO.Path.Combine(strDirectory, strTable + ".xml"), settings)) { 

      // Strip out timezone 
      foreach (DataTable dt in ds.Tables) { 

       foreach (DataColumn dc in dt.Columns) { 

        if (object.ReferenceEquals(dc.DataType, typeof(DateTime))) { 
         dc.DateTimeMode = DataSetDateTime.Unspecified; 
        } 

       } 

      } 

      ds.Tables[0].WriteXml(w, XmlWriteMode.IgnoreSchema); 
      w.Flush(); 
      w.Close(); 
     } 
     // w 

    } 
    // ds 

} 
// ExportTable 

VB.NET originale:

Public Sub ExportTable(strDirectory As String, dtt As DataTable) 
     Using ds As New System.Data.DataSet() 
      Dim strTable As String = dtt.TableName 

      ds.Tables.Add(dtt) 
      ds.DataSetName = strTable 

      ' Move data to attributes 
      For Each dt As DataTable In ds.Tables 

       For Each dc As DataColumn In dt.Columns 
        dc.ColumnMapping = MappingType.Attribute 
       Next dc 

      Next dt 

      Dim settings As New System.Xml.XmlWriterSettings() 
      settings.Indent = True 
      'settings.Encoding = System.Text.Encoding.GetEncoding("ISO-8859-1") 
      settings.Encoding = System.Text.Encoding.UTF8 
      settings.CloseOutput = True 
      settings.CheckCharacters = True 
      settings.NewLineChars = vbCrLf ' vbCr & vbLf 

      ' Write as UTF-8 with indentation 
      Using w As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(System.IO.Path.Combine(strDirectory, strTable & ".xml"), settings) 

       ' Strip out timezone 
       For Each dt As DataTable In ds.Tables 

        For Each dc As DataColumn In dt.Columns 

         If dc.DataType Is GetType(DateTime) Then 
          dc.DateTimeMode = DataSetDateTime.Unspecified 
         End If 

        Next dc 

       Next dt 

       ds.Tables(0).WriteXml(w, XmlWriteMode.IgnoreSchema) 
       w.Flush() 
       w.Close() 
      End Using ' w 

     End Using ' ds 

    End Sub ' ExportTable 
+1

Utilizzare XmlWriteMode.WriteSchema. Con uno schema incluso, non importa che non aggiunga attributi per valori nulli. – JamieSee

+0

@JamieSee: Senza schema sarebbe più bello, perché il risultato dovrebbe essere facile da leggere in qualsiasi linguaggio di programmazione, non solo .NET. –

risposta

8

Ogni attributo XML deve essere assegnato un valore che è racchiuso in una coppia di virgolette singole o doppie. Non esiste un equivalente in testo normale per indicare un valore NULL. Una coppia di virgolette senza valore per rappresentare una stringa vuota non è uguale a un valore NULL. Pertanto, l'unico modo per rappresentare un attributo NULL è di omettere l'attributo.

Ciò significa che è necessario impostare AllowDBNull su falso e assegnare un opportuno DefaultValue a DataColumn o includere lo schema.

Vedere anche Handling Null Values (ADO.NET)., in particolare questa sezione, che illustra il comportamento:

Inoltre, valgono le seguenti regole per un'istanza di DataRow [ "columnName"] assegnazioni nulli:.

1 . Il valore predefinito predefinito è DbNull.Value per tutti tranne le colonne Null fortemente tipizzate in cui è il valore nullo fortemente digitato.

2. I valori Null non vengono mai scritti durante la serializzazione in file XML (come in "xsi: nil").

3. Tutti i valori non nulli, compresi i valori predefiniti, vengono sempre scritti durante la serializzazione in XML. A differenza della semantica XSD/XML in cui un valore nullo (xsi: nil) è esplicito e il valore predefinito è implicito (se non è presente in XML, un parser di convalida può ottenerlo da uno schema XSD associato). È vero il contrario per un DataTable: un valore nullo è implicito e il valore predefinito è esplicito.

4. Tutti i valori di colonna mancanti per le righe lette dall'input XML vengono assegnati NULL. Alle righe create utilizzando NewRow o metodi simili viene assegnato il valore predefinito di DataColumn .

5. Il metodo IsNull restituisce true per DbNull.Value e INullable.Null.

2

Prova a impostare la colonna DefaultValue a qualcosa di valido

foreach (DataTable dt in ds.Tables) { 

     foreach (DataColumn dc in dt.Columns) { 
      dc.ColumnMapping = MappingType.Attribute; 
      //If type is DataType string 
      dc.DefaultValue = String.Empty; 
     } 
2

due punti:

Primi: L'ExportTable() ha generato un'eccezione: "DataTable già appartiene ad un altro gruppo di dati."Quando ho eseguito:

ds.Tables.Add(dtt) 

ho corretto questo facendo una copia locale della tabella:.

Dim dtX As DataTable = dtt.Copy 
ds.Tables.Add(dtX) 
ds.DataSetName = strTable 

Questo ha funzionato bene

Secondo: Se si utilizza l'XML per creare un istruzione SQL dinamica, non è necessario preoccuparsi delle colonne/campi con il valore NULL che viene omesso nell'esportazione XML. È sufficiente scorrere gli attributi nel record XML, creare l'istruzione INSERT o UPDATE ed eseguire un comando di connessione Questo è più veloce di Us ng un DataSet.

Per INSERT ha uno svantaggio. Se la chiave primaria viene creata incrementando una colonna Identity, un DataSet ADO.Net lo restituirà. SQL dinamico richiede un'istruzione SELECT per recuperarlo.

Inoltre, sarebbe una buona idea offuscare il codice.

1

E 'un filo po' vecchio, ma, forse può aiutare qualcuno:
Se non si sta scrivendo i file grandi xml frequentemente (va bene per le impostazioni o qualcosa di simile esportazione) è possibile utilizzare la funzione di sotto, altrimenti è meglio usare cutom schema xml.

private static void addEmptyElementsToXML(DataSet dataSet) 
{ 
    foreach (DataTable dataTable in dataSet.Tables) 
    { 
     foreach (DataRow dataRow in dataTable.Rows) 
     { 
      for (int j = 0; j < dataRow.ItemArray.Length; j++) 
      { 
       if (dataRow.ItemArray[j] == DBNull.Value) 
        dataRow.SetField(j, string.Empty); 
      } 
     } 
    } 
} 

Usage:

using(DataTable dTable = ..something..) 
using(DataSet dS = new DataSet()) 
using(XmlTextWriter xmlStream = new XmlTextWriter("FILENAME.XML", Encoding.UTF8)) 
{ 
    //set xml to be formatted so it can be easily red by human 
    xmlStream.Formatting = Formatting.Indented; 
    xmlStream.Indentation = 4; 

    //add table to dataset 
    dS.Tables.Add(dTable); 

    //call the mentioned function so it will set all DBNull values in dataset 
    //to string.Empty 
    addEmptyElementsToXML(dS); 

    //write xml to file 
    xmlStream.WriteStartDocument(); 
    dS.WriteXml(xmlStream); 
} 
0

ho cercato tutto il mondo per una soluzione di scrittura campi nulli a XML utilizzando DataSet.WriteXML(). Ho scoperto che i seguenti lavori sono ottimizzati in termini di prestazioni. Ho creato una funzione per la vostra comodità. Modificare le tabelle dei set di dati uno dopo l'altro chiamando la seguente funzione e sostituendo le tabelle.

private DataTable GetNullFilledDataTableForXML(DataTable dtSource) 
{ 
    // Create a target table with same structure as source and fields as strings 
    // We can change the column datatype as long as there is no data loaded 
    DataTable dtTarget = dtSource.Clone(); 
    foreach (DataColumn col in dtTarget.Columns) 
     col.DataType = typeof(string); 

    // Start importing the source into target by ItemArray copying which 
    // is found to be reasonably fast for null operations. VS 2015 is reporting 
    // 500-525 milliseconds for loading 100,000 records x 10 columns 
    // after null conversion in every cell 
    // The speed may be usable in many circumstances. 
    // Machine config: i5 2nd Gen, 8 GB RAM, Windows 7 64bit, VS 2015 Update 1 
    int colCountInTarget = dtTarget.Columns.Count; 
    foreach (DataRow sourceRow in dtSource.Rows) 
    { 
     // Get a new row loaded with data from source row 
     DataRow targetRow = dtTarget.NewRow(); 
     targetRow.ItemArray = sourceRow.ItemArray; 

     // Update DBNull.Values to empty string in the new (target) row 
     // We can safely assign empty string since the target table columns 
     // are all of string type 
     for (int ctr = 0; ctr < colCountInTarget; ctr++) 
      if (targetRow[ctr] == DBNull.Value) 
       targetRow[ctr] = String.Empty; 

     // Now add the null filled row to target datatable 
     dtTarget.Rows.Add(targetRow); 
    } 

    // Return the target datatable 
    return dtTarget; 
}