XML
Serialisering
Børre Stenseth
Moduler>XML>XML Vedlikehold

Vedlikehold av en XML-fil

Hva
Image1
Vedlikehold av en enkel XML-fil

Vi ønsker å lage et program som vedlikeholder en enkel XML-fil.

Vi kan gjøre dette på mange måter. En måte er å lese inn fila, etablere DOM-treet og respondere på brukerens aksjoner ved å oppdatere treet (fjerne, legge til og endre noder).

Vi vil imidlertid benytte oss av en mulighet i C# som lar oss serialisere (skrive til fil) klasser som XML. Det vil si at vi kunne likegodt snu problemstillingen og si at vi ønsker å ta vare på en klassestruktur fra en programsesjon til den neste.

Programmet er bygget opp av tre klasser: Personer, Form1 og NewDialog. Personer realiserer den strukturen som bærer XML-dataene, mens Form1 som vanlig er implementasjon av hovedvinduet. NewDialog er en enkel dialogboks for å registrere nye personer.

Fila vi skal bruke inneholde navn og adresse på en rekke personer, og skal bygges etter følgende struktur:

<?xml version="1.0"?>
<personer>
  <person navn="Ole- Petter" adresse="Halden" />
  <person navn="Ole-Johan" adresse="Halden" />
</personer>

Personer

La oss først se på klassen Personer:

using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text;
namespace maintainXML
{
    [XmlRoot("personer")] public class Personer
    {
        private ArrayList personList;
        public Personer()
        {
            personList = new ArrayList();
        }
        [XmlElement("person")] public onePerson[] Items
        {
            get
            {
                onePerson[] Items = new onePerson[personList.Count];
                personList.CopyTo(Items);
                return Items;
            }
            set
            {
                if (value == null)
                    return;
                onePerson[] Items = (onePerson[])value;
                personList.Clear();
                foreach (onePerson Item in Items)
                    personList.Add(Item);
            }
        }
        public int AddPerson(onePerson Item)
        {
            return personList.Add(Item);
        }
        public class onePerson
        {
            [XmlAttribute("navn")]      public string name;
            [XmlAttribute("adresse")]   public string address;
            
            public onePerson(){    }
            public onePerson(string Name, string Address)
            {
                name = Name;
                address = Address;
            }
            public override string ToString()
            {
                return name;
            }
        }
    }
}

Merk den spesielle syntaksen som brukes som merkelapper på noen klasser og attributter:

  • [XmlRoot("personer")] public class Personer
  • [XmlElement("person")] public onePerson[] Items
  • [XmlAttribute("navn")] public string name;
  • [XmlAttribute("adresse")] public string address;

Når vi identerer slik som over, ser vi at angivelsene knytter de respektive programelementene til den XML-strukturen vi har sagt vi skal anvende.

Lesing av en fil og opprettelse av en liste, Alle, med personer, kan se slik ut:

    XmlSerializer s = new XmlSerializer(typeof(Personer));
    System.IO.TextReader r = new System.IO.StreamReader(filename);
    Alle=(Personer)s.Deserialize(r);
    r.Close();

Serialisering av en liste med personer, Alle, kan se slik ut:

    XmlSerializer s = new XmlSerializer(typeof(Personer));
    System.IO.TextWriter w = new System.IO.StreamWriter(filename);
    s.Serialize(w,Alle);
    w.Close();

Begge kodefragmentene inngår i Form nedenfor.

Form

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;
namespace maintainXML
{
    public partial class Form1 : Form
    {
        // the personlist
        Personer Alle;
        string filename;
        public Form1()
        {
            InitializeComponent();
            // Get data
            filename = Path.Combine(Application.StartupPath, 
                                    "data.xml");
            if (System.IO.File.Exists(filename))
            {
                // get the data
                XmlSerializer s = new XmlSerializer(typeof(Personer));
                System.IO.TextReader r = new System.IO.StreamReader(filename);
                Alle=(Personer)s.Deserialize(r);
                r.Close();
            }
            else
            {
                // produce a dummyperson to start with
                Alle = new Personer();
                Alle.AddPerson(new Personer.onePerson("Ole Brumm",
                                                      "100meterskogen"));
            }            
            // Fill up the listBoks            
            listBox1.Items.AddRange(Alle.Items);
            // and mark first as selected
            if (listBox1.Items.Count > 0)
            {
                listBox1.SetSelected(0, true);
                ShowItem(0);
            }
            // disable the update button
            buttonUpdate.Enabled=false;
        }
        private void ShowItem(int ix)
        {
            // fill the textboxes
            if (ix == -1)
            {
                textBoxName.Text = "";
                textBoxAddress.Text = "";
            }
            else
            {
                textBoxName.Text = 
                    ((Personer.onePerson)listBox1.Items[ix]).name;
                textBoxAddress.Text = 
                    ((Personer.onePerson)listBox1.Items[ix]).address;
            }
            buttonUpdate.Enabled = false;
        }
        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            ShowItem(listBox1.SelectedIndex);
        }
        private void buttonRemove_MouseClick(object sender, MouseEventArgs e)
        {
            // Remove the selected person and select next if possible
            int ix = listBox1.SelectedIndex;
            listBox1.Items.RemoveAt(ix);
            if (listBox1.Items.Count == 0)
                ShowItem(-1);
            else
            {
                if (listBox1.Items.Count <= ix)
                    ix = 0;
                listBox1.SetSelected(ix, true);
                ShowItem(ix);
            }
            listBox1.Refresh();
        }
        
        private void buttonNew_MouseClick(object sender, MouseEventArgs e)
        {
            // Add a new person, use a dialogbox
            NewDialog d = new NewDialog();
            DialogResult dr=d.ShowDialog(this);
            if (dr==DialogResult.OK)
            {
                Alle.AddPerson(new Personer.onePerson(d.theName,d.theAddress));
                listBox1.Items.Clear();
                listBox1.Items.AddRange(Alle.Items);
                listBox1.SetSelected(listBox1.Items.Count - 1, true);
                ShowItem(listBox1.Items.Count - 1);
            }
            d.Hide();
            d.Dispose();
        }
        private void buttonUpdate_MouseClick(object sender, MouseEventArgs e)
        {
            // Update data for the selected person
            int ix = listBox1.SelectedIndex;
            ((Personer.onePerson)listBox1.Items[ix]).name = 
                textBoxName.Text;
            ((Personer.onePerson)listBox1.Items[ix]).address = 
                textBoxAddress.Text;
            listBox1.Items.Clear();
            listBox1.Items.AddRange(Alle.Items);
            buttonUpdate.Enabled = false;
        }
        private void textBoxName_TextChanged(object sender, EventArgs e)
        {
            // Name is changed
            buttonUpdate.Enabled = textBoxName.Text.Length>0;
        }
        private void textBoxAddress_TextChanged(object sender, EventArgs e)
        {
            // Address is changed
            buttonUpdate.Enabled = true;
        }
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Whatever is in the listbox is moved to Alle:
            Personer.onePerson[] ps=
                new Personer.onePerson[listBox1.Items.Count];
            new System.Collections.ArrayList(listBox1.Items).CopyTo(ps);
            Alle.Items=ps;
                                    
            // Serialize it
            XmlSerializer s = new XmlSerializer(typeof(Personer));
            System.IO.TextWriter w = new System.IO.StreamWriter(filename);
            s.Serialize(w,Alle);
            w.Close();
            this.Dispose(true);
        }
    }
}

NewDialog

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace maintainXML
{
    public partial class NewDialog : Form
    {
        public NewDialog()
        {
            InitializeComponent();
            buttonOK.Enabled=false;
        }
        public string theName
        {
            get { return textBoxName.Text; }
        }
        public string theAddress
        {
            get { return textBoxAddress.Text; }
        }
        
        private void textBoxName_TextChanged(object sender, EventArgs e)
        {
            buttonOK.Enabled = 
                (textBoxName.Text.Length > 0) && 
                (textBoxAddress.Text.Length > 0);
        }
        private void textBoxAddress_TextChanged(object sender, EventArgs e)
        {
            buttonOK.Enabled = (
                textBoxName.Text.Length > 0) && 
                (textBoxAddress.Text.Length > 0);
        }
    }
}

Datafil

Datafila som lagres, data.xml, kan se slik ut:

<?xml version="1.0" encoding="utf-8"?>
<personer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <person navn="Ole" adresse="Halden" />
  <person navn="Ole-Johan" adresse="Halden" />
  <person navn="Hans Kristian" adresse="Moss" />
  <person navn="Marit" adresse="Drammen" />
  <person navn="Johanne" adresse="Moss" />
  <person navn="Jacob Olav" adresse="Her og der" />
  <person navn="Peder Clausen" adresse="Bodø" />
  <person navn="Per" adresse="Aremark" />
  <person navn="Pedersen" adresse="kinsarvik" />
  <person navn="jakobsen" adresse="tokyo" />
</personer>
Referanser
  • Prosjekt:
    https://svn.hiof.no/svn/psource/Csharpspikes/xmlmaintain
  • Merk hjelpeprogrammet xsd.exe som følger med .Net. Det kan hjelpe deg å etablere sammenhenger mellom klasser og XML-strukturer.
Vedlikehold
B.Stenseth, revidert desember 2006
(Velkommen) Moduler>XML>XML Vedlikehold (XPATH og sprint)