Acquisizione ricorsiva dei dati della tabella relazionale in XML

1

Ho livelli di tabelle (Livello1, Livello2, Livello3, ...) Per semplicità, diremo che ho 3 livelli.

Le righe nelle tabelle di livello superiore sono i genitori delle righe della tabella di livello inferiore. La relazione non salta comunque i livelli. Per esempio. Row1Level1 è padre di Row3Level2, Row2Level2 è padre di Row4Level3. Il genitore di Livello (n-1) è sempre in Livello (n).

Date queste tabelle con i dati, ho bisogno di trovare una funzione ricorsiva che generi un file XML per rappresentare la relazione e i dati. Per esempio.

<data>
  <level levelid = 1 rowid=1>
    <level levelid = 2 rowid=3 />
  </level>
  <level levelid = 2 rowid=2>
    <level levelid = 3 rowid=4 />
  </level>
</data>  

Mi piacerebbe ricevere aiuto con uno pseudo-codice per questa configurazione. Questo è quello che ho finora:

Codice modificato / aggiornato:

XElement GetXML(ArrayList list, XElement xml)
        {
            XElement xElement = xml;
            // Get the rows of the current list into xmlData format
            ArrayList childList = GetChildList(list);

            if (childList != null)
            {
                foreach (Row r in list)
                {
                    // Convert the rows to xml
                    xElement = new XElement(xml);
                    xElement.AddFirst(new XElement("Row"));
                    xElement.Add(new XAttribute("RowId", r.RowId));

                    // Get the children as xml
                    if (childList != null)
                    {
                        ArrayList childRows = GetChildRows(list);
                        XElement childXml = GetXML(childRows, xElement);
                        if (childXml != null)
                        {
                            xElement.Add(childXml);
                        }
                    }
                }
            }
            return xElement;
       }

Questa è la funzione principale. Posso aggiungere il resto del programma di test, se necessario. Grazie.

static void Main(string[] args)
        {
            Program p = new Program();

            // Initialize datasets
            p.InitializeLists();

            // Create xml map of the datasets
            XElement x = p.GetXML(p.level1, new XElement("Row"));

            Console.WriteLine(x.ToString());
            Console.ReadLine();
        }
    
posta Tom 28.10.2013 - 16:10
fonte

2 risposte

1

Prima di tutto, questo dovrebbe probabilmente essere spostato nel esame del codice del sito.

Preferirei venire al problema da una diversa angolazione, usando gli oggetti per gestire il comportamento piuttosto che un metodo ricorsivo che sa come attraversare tutto. In questo modo sembra mappare meglio il tuo dominio, con un bonus che elimina la necessità di un valore ListID memorizzato su ogni riga, migliorando la scalabilità.

public class Row
{
   public ICollection<Row> ChildRows { get; private set; }
   public int RowId { get; private set; }
   public int ParentRowId { get; private set; }

   public Row(int rowId, int parentRowId)
   {
      RowId = rowId;
      ParentRowId = parentRowId;
      ChildRows = new List<Row> ();
   }

   public XElement BuildXML(int level)
   {
      var element = new XElement("level",
                                 new XAttribute("levelid", level),
                                 new XAttribute("rowid", RowId));
      if (ChildRows.Any())
         element.Add(ChildRows.Select(r => r.BuildXML(level + 1)));

      return element;
   }
}

Vorrei quindi costruire la mia gerarchia di oggetti come una struttura ad albero. Per farlo facilmente, inizializzo con due raccolte: una un elenco degli elementi di primo livello e l'altro un dizionario di tutti gli elementi:

void InitializeLists()
{
   var allRows = new List<Row>
                 {
                    new Row(1, 0),
                    new Row(2, 0),
                    new Row(3, 0),
                    new Row(1, 1),
                    new Row(2, 2),
                    new Row(3, 2),
                    new Row(4, 3),
                    new Row(5, 1),
                    new Row(6, 1),
                    new Row(1, 3),
                    new Row(2, 3),
                    new Row(3, 4),
                    new Row(4, 6),
                    new Row(5, 6),
                    new Row(6, 6),
                 }; 
   var rowTable = allRows.ToDictionary(r => r.RowId);

   var rootRows = new List<Row>();
   foreach(var row in allRows)
   {
      if(row.RowId == 0)
         rootRows.Add(row);
      else
      {
         Row parentRow;

         if(rowTable.TryGetValue(row.ParentRowId, out parentRow))
         {
            parentRow.ChildRows.Add(row);
         }
         else
         {
            // throw or log an appropriate error
         }
      }
   }

   _rootRows = rootRows;
}

Fatto ciò, il tuo edificio XML diventa piuttosto banale:

public static void Main(string[] args)
{
   var p = new Program();

   // Initialize datasets
   p.InitializeLists();

   // Create xml map of the datasets
   XElement x = p.GetXML();

   Console.WriteLine(x.ToString());
   Console.ReadLine();
}

public XElement GetXML()
{
   var xElement = new XElement("Rows");
   foreach(var row in _rootRows)
      xElement.Add(row.BuildXML(1));

   return xElement;
}

Aggiorna

Non ho notato nel mio primo tentativo che gli ID delle righe non erano univoci. Ci sono due opzioni di base per affrontare questo:

  • Rendi gli ID univoci
  • Crea una chiave composita per la ricerca che sarà unica. L'opzione più semplice è quella di reintrodurre gli ID degli elenchi e creare una chiave come combinazione di ID e ID di elenco.

Poiché gli esempi precedenti gestiscono i casi con ID di riga univoci, aggiungerò alcune modifiche per la seconda opzione. Innanzitutto, reintroduciamo ListId nella riga. Questo ci consente di rimuovere il parametro da BuildXML:

public class Row
{
   public ICollection<Row> ChildRows { get; private set; }
   public int RowId { get; private set; }
   public int ListId { get; private set; }
   public int ParentRowId { get; private set; }

   public Row(int rowId, int listId, int parentRowId)
   {
      RowId = rowId;
      ListId = listId;
      ParentRowId = parentRowId;
      ChildRows = new List<Row> ();
   }

   public XElement BuildXML()
   {
      var element = new XElement("level",
                                 new XAttribute("levelid", ListId),
                                 new XAttribute("rowid", RowId));
      if (ChildRows.Any())
         element.Add(ChildRows.Select(r => r.BuildXML()));

      return element;
   }
}

Quindi rifaremo come inizializziamo:

void InitializeLists()
{
   var allRows = new List<Row>
                 {
                    new Row(1, 1, 0),
                    new Row(2, 1, 0),
                    new Row(3, 1, 0),
                    new Row(1, 2, 1),
                    new Row(2, 2, 2),
                    new Row(3, 2, 2),
                    new Row(4, 2, 3),
                    new Row(5, 2, 1),
                    new Row(6, 2, 1),
                    new Row(1, 3, 3),
                    new Row(2, 3, 3),
                    new Row(3, 3, 4),
                    new Row(4, 3, 6),
                    new Row(5, 3, 6),
                    new Row(6, 3, 6),
                 };

   const string FMT = "{0}:{1}";

   var rowTable = allRows.ToDictionary(r => string.Format(FMT, r.ListId, r.RowId));

   var rootRows = new List<Row>();
   foreach(var row in allRows)
   {
      if(row.ListId == 1)
         rootRows.Add(row);
      else
      {
         Row parentRow;

         var parentKey = string.Format(FMT, row.ListId-1, row.ParentRowId);

         if(rowTable.TryGetValue(parentKey, out parentRow))
         {
            parentRow.ChildRows.Add(row);
         }
      }
   }

   _rootRows = rootRows;
}

Infine, risolviamo la funzione GetXML in modo che non passi a un livello iniziale:

XElement GetXML()
{
   XElement xElement = new XElement("Rows");
   foreach(var row in _rootRows)
      xElement.Add(row.BuildXML());

   return xElement;
}
    
risposta data 29.10.2013 - 19:41
fonte
0

Ecco l'intera soluzione. Questo ha funzionato. Grazie per la ricerca:)

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Xml.Linq;

namespace RecursiveExcursion
{
    class Program
    {
        ArrayList level1 = new ArrayList();
        ArrayList level2 = new ArrayList();
        ArrayList level3 = new ArrayList();

        static void Main(string[] args)
        {
            Program p = new Program();

            // Initialize datasets
            p.InitializeLists();

            // Create xml map of the datasets
            XElement x = p.GetXML(p.level1, new XElement("Rows"));

            Console.WriteLine(x.ToString());
            Console.ReadLine();
        }

        void InitializeLists()
        {
            // Add rows to the first list
            level1.Add(new Row(1, 1, 0));
            level1.Add(new Row(1, 2, 0));
            level1.Add(new Row(1, 3, 0));

            // Add rows to the second list
            level2.Add(new Row(2, 1, 1));
            level2.Add(new Row(2, 2, 2));
            level2.Add(new Row(2, 3, 2));
            level2.Add(new Row(2, 4, 3));
            level2.Add(new Row(2, 5, 1));
            level2.Add(new Row(2, 6, 1));

            // Add rows to the third list
            level3.Add(new Row(3, 1, 3));
            level3.Add(new Row(3, 2, 3));
            level3.Add(new Row(3, 3, 4));
            level3.Add(new Row(3, 4, 6));
            level3.Add(new Row(3, 5, 6));
            level3.Add(new Row(3, 6, 6));
        }

        XElement GetXML(ArrayList list, XElement xml)
        {
            XElement xElement = xml;

            // Get the rows of the current list into xmlData format
            ArrayList childList = GetChildList(list);

            if (childList != null)
            {
                foreach (Row r in list)
                {
                    // Convert the rows to xml
                    XElement xElementRow = new XElement("Row");
                    xElementRow.Add(new XAttribute("RowId", r.RowId));

                    // Get the children as xml
                    ArrayList childRows = GetChildRows(list, r);
                    xElement.Add(GetXML(childRows, xElementRow));
                }
            }
            else
            {
                foreach (Row r in list)
                {
                    XElement xElementRow = new XElement("Row");
                    xElementRow.Add(new XAttribute("RowId", r.RowId));
                    xElement.Add(xElementRow);
                }
            }
            return xElement;
        }

        private ArrayList GetChildRows(ArrayList list, Row r)
        {
            ArrayList childList = GetChildList(list);
            ArrayList childRows = new ArrayList();

            if (childList != null)
            {
                foreach (Row childRow in childList)
                {
                    if (childRow.ParentRowId == r.RowId)
                    {
                        childRows.Add(childRow);
                    }
                }
                return childRows;
            }
            else
            {
                return null;
            }
        }

        private ArrayList GetChildList(ArrayList list)
        {
            if (list.Count > 0)
            {
                int listName = 0;

                Row firstRow = (Row)list[0];
                listName = firstRow.ListId;

                switch (listName)
                {
                    case 1:
                        {
                            return this.level2;
                        }
                    case 2:
                        {
                            return this.level3;
                        }
                    case 3:

                    default:
                        {
                            return null;
                        }
                }

            }
            return null;

        }

        bool IsLastLevel(int listId)
        {
            return listId == 3;
        }
    }

    class Row
    {
        public int ListId { get; set; }
        public int RowId { get; set; }
        public int ParentRowId { get; set; }

        public Row()
        {
        }

        public Row(int listId, int rowId, int parentRowId)
        {
            ListId = listId;
            RowId = rowId;
            ParentRowId = parentRowId;
        }
    }
}
    
risposta data 29.10.2013 - 19:00
fonte

Leggi altre domande sui tag