Råmaterialet er beskevet i modulen Noen datasett, og har denne strukturen
<?xml version="1.0" encoding="ISO-8859-1"?>
<IOC>
<OlympicGame place="Barcelona" year="1992">
<event dist="100m">
<athlet>
<name>Dennis Mitchell</name>
<nation>USA</nation>
<result>10.04</result>
</athlet>
...
</event>
...
</OlympicGame>
...
</IOC>
I sin helhet:
Hvem er hvem
Vi ønsker å lage en liste av deltagere (athlets) der hver deltager er representert
kun en gang. Vi baserer oss da på at navnet er god nok identifikasjon.
Koden for å etablere lista og finne en deltagers deltagelse blir slik:
// adding name of athlets to listbox
private void buttonAthlets_Click(object sender, EventArgs e)
{
if (m_Doc == null) { return; }
XmlNodeList alist = m_Doc.SelectNodes("//name");
foreach (XmlElement aelt in alist)
{
String nText = aelt.InnerText;
// already there ?
if (listBoxSelection.Items.IndexOf(nText) == -1)
listBoxSelection.Items.Add(nText);
}
}
// selecting an athletname, report all performances
private void listBoxSelection_SelectedIndexChanged(object sender, EventArgs e)
{
String t = listBoxSelection.SelectedItem.ToString();
String xpath = String.Format("//athlet[name='{0}']", t);
XmlNodeList same = m_Doc.SelectNodes(xpath);
textBox1.Text = "";
foreach (XmlElement a in same)
{
textBox1.Text += String.Format("{0} i {1} - {2}\r\n",
((XmlElement)a.ParentNode).GetAttribute("dist"),
((XmlElement)a.ParentNode.ParentNode).GetAttribute("place"),
((XmlElement)a.ParentNode.ParentNode).GetAttribute("year"));
}
}
Validering
Vi ønsker å validere fila med resultater. Vi kan gjøre dette på mange måter. Her bruker
vi en enkel framgangsmåte:
- last opp XML-fila i Visual Studio
- fra menyen XML, velg Create Schema
- lage denne i prosjektkatalogen
- adder schema-fila til prosjektet
- sørg for at både xml-fila og schema-fila kopieres til den katalogen applikasjonen kjøres
String XSDFILEPATH =
Path.Combine(Application.StartupPath, "all_results.xsd");
String XMLFILEPATH =
Path.Combine(Application.StartupPath, "all_results.xml");
Følgende kode etablerer XmlDocumentet, viser det i en webbrowser kontrol og validerer.
private void postInitialize()
{
try
{
String filename=Path.Combine(Application.StartupPath,
"all_results.xml");
webBrowser1.Url = new Uri(XMLFILEPATH);
m_Doc = new XmlDocument();
// will fail if malformed or not found
m_Doc.Load(XMLFILEPATH);
m_Doc.Normalize();
// validate
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, XSDFILEPATH);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler +=
new ValidationEventHandler(SchemaValidationEventHandler);
XmlReader rdr =
XmlReader.Create(new StringReader(m_Doc.InnerXml), settings);
while (rdr.Read()) { }
}
catch (Exception ex)
{
webBrowser1.DocumentText =
String.Format(@"<html><body>
<p>Sorry:</p>
<p>{0}</p>
</body></html>", ex.Message);
m_Doc = null;
}
}
Merk metoden SchemaValidationEventHandler som gor oss advarsler ogfeilmeldinger under valideringen.
// pick up validation errors/warnngs
// report in text box
private void SchemaValidationEventHandler(object sender, ValidationEventArgs e)
{
switch (e.Severity)
{
case XmlSeverityType.Error:
textBox1.Text += String.Format("\r\nError: {0}", e.Message);
break;
case XmlSeverityType.Warning:
textBox1.Text += String.Format("\r\nWarning: {0}", e.Message);
break;
}
}
Vi har valgt et enkelt demonstarsjonseksempel, laget schema-fila ukritisk og brukt den uten endringer.
I de fleste tilfelle ønsker vi å validere filer som er laget eller importert fra programmer vi ikke
har kontroll over. En fornuftig framgangsmåter er trolig å lage en schema-fil basert på et eksempel
og deretter editere denne for hånd for å generalisere og eller presisere.
Den schema-fila vi får laget i dette eksempelet ser slik ut:
<?xml version="1.0" encoding="iso-8859-1"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="IOC">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="OlympicGame">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="event">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="athlet">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="nation" type="xs:string" />
<xs:element name="result" type="xs:decimal" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="dist" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="place" type="xs:string" use="required" />
<xs:attribute name="year" type="xs:unsignedShort" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
For å kunne bearbeide denne må vi sette oss inn hvordan xsd-filer er bygget opp.
Vi forfølger ikke dette her, men det er mye å hente både i presisering av lovlige verdirer og
i generalisering av hvordan validerbare filer kan bygges opp.
Er det f.eks. viktig at elementene name, nation,result kommer i nøyaktig den rekkefølgen ?
En modifisert versjon kan se slik ut:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="AT_3">
<xs:restriction base="xs:string">
<xs:enumeration value="100m"/>
<xs:enumeration value="200m"/>
<xs:enumeration value="400m"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="AT_2">
<xs:restriction base="xs:short">
<xs:enumeration value="1992"/>
<xs:enumeration value="1996"/>
<xs:enumeration value="2000"/>
<xs:enumeration value="2004"/>
<xs:enumeration value="2008"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="AT_1">
<xs:restriction base="xs:string">
<xs:enumeration value="Athens"/>
<xs:enumeration value="Atlanta"/>
<xs:enumeration value="Barcelona"/>
<xs:enumeration value="Beijing"/>
<xs:enumeration value="Sidney"/>
</xs:restriction>
</xs:simpleType>
<xs:attribute name="year" type="AT_2"/>
<xs:attribute name="dist" type="AT_3"/>
<xs:attribute name="place" type="AT_1"/>
<xs:element name="result" type="xs:decimal"/>
<xs:element name="nation" type="xs:string"/>
<xs:element name="name" type="xs:string"/>
<xs:element name="event">
<xs:complexType>
<xs:sequence>
<xs:element ref="athlet" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute ref="dist" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="athlet">
<xs:complexType>
<xs:sequence>
<xs:element ref="name"/>
<xs:element ref="nation"/>
<xs:element ref="result"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="OlympicGame">
<xs:complexType>
<xs:sequence>
<xs:element ref="event" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute ref="year" use="required"/>
<xs:attribute ref="place" use="required"/>
</xs:complexType>
</xs:element>
<xs:element name="IOC">
<xs:complexType>
<xs:sequence>
<xs:element ref="OlympicGame" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Koden i Formen er i sin helhet slik:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.IO;
using System.Windows.Forms;
namespace allxml2
{
public partial class Form1 : Form
{
XmlDocument m_Doc = null;
String XSDFILEPATH =
Path.Combine(Application.StartupPath, "all_results.xsd");
//String XSDFILEPATH =
// Path.Combine(Application.StartupPath, "modified.xsd");
String XMLFILEPATH =
Path.Combine(Application.StartupPath, "all_results.xml");
public Form1()
{
InitializeComponent();
postInitialize();
}
#region setup
private void postInitialize()
{
try
{
String filename=Path.Combine(Application.StartupPath,
"all_results.xml");
webBrowser1.Url = new Uri(XMLFILEPATH);
m_Doc = new XmlDocument();
// will fail if malformed or not found
m_Doc.Load(XMLFILEPATH);
m_Doc.Normalize();
// validate
XmlReaderSettings settings = new XmlReaderSettings();
settings.Schemas.Add(null, XSDFILEPATH);
settings.ValidationType = ValidationType.Schema;
settings.ValidationEventHandler +=
new ValidationEventHandler(SchemaValidationEventHandler);
XmlReader rdr =
XmlReader.Create(new StringReader(m_Doc.InnerXml), settings);
while (rdr.Read()) { }
}
catch (Exception ex)
{
webBrowser1.DocumentText =
String.Format(@"<html><body>
<p>Sorry:</p>
<p>{0}</p>
</body></html>", ex.Message);
m_Doc = null;
}
}
#endregion setup
#region errors
// pick up validation errors/warnngs
// report in text box
private void SchemaValidationEventHandler(object sender, ValidationEventArgs e)
{
switch (e.Severity)
{
case XmlSeverityType.Error:
textBox1.Text += String.Format("\r\nError: {0}", e.Message);
break;
case XmlSeverityType.Warning:
textBox1.Text += String.Format("\r\nWarning: {0}", e.Message);
break;
}
}
#endregion errors
#region naming
// adding name of athlets to listbox
private void buttonAthlets_Click(object sender, EventArgs e)
{
if (m_Doc == null) { return; }
XmlNodeList alist = m_Doc.SelectNodes("//name");
foreach (XmlElement aelt in alist)
{
String nText = aelt.InnerText;
// already there ?
if (listBoxSelection.Items.IndexOf(nText) == -1)
listBoxSelection.Items.Add(nText);
}
}
// selecting an athletname, report all performances
private void listBoxSelection_SelectedIndexChanged(object sender, EventArgs e)
{
String t = listBoxSelection.SelectedItem.ToString();
String xpath = String.Format("//athlet[name='{0}']", t);
XmlNodeList same = m_Doc.SelectNodes(xpath);
textBox1.Text = "";
foreach (XmlElement a in same)
{
textBox1.Text += String.Format("{0} i {1} - {2}\r\n",
((XmlElement)a.ParentNode).GetAttribute("dist"),
((XmlElement)a.ParentNode.ParentNode).GetAttribute("place"),
((XmlElement)a.ParentNode.ParentNode).GetAttribute("year"));
}
}
#endregion naming
}
}
Enkelt valideringseksempel
Et enkelt eksempel som gjør det lett å eksperimentere med schemaer og validering.
Eksempelet er bygget rundt de samme dataene som over, men programmet gjør ikke annet enn å laste
en XML-fil og validere den.
Koden i Formen er i sin helhet slik:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using System.IO;
namespace XMLValidate
{
public partial class Form1 : Form
{
XmlDocument doc=null;
String validationMsg;
public Form1()
{
InitializeComponent();
prepare();
}
private void prepare()
{
// if you want to validate the original:
//textBoxURL.Text = "http://www.it.hiof.no/~borres/commondata/olympiade/all_results.xml";
textBoxURL.Text = Path.Combine(Application.StartupPath, "results.xml");
// if you want to validate against the original schema:
//textBoxSchema.Text = "http://www.it.hiof.no/~borres/commondata/olympiade/olymp.xsd";
textBoxSchema.Text = Path.Combine(Application.StartupPath,"schema.xsd");
}
private void buttonLoad_Click(object sender, EventArgs e)
{
String source = textBoxURL.Text;
try
{
doc = new XmlDocument();
doc.Load(source);
textBoxLoad.Text = "Loaded and parsed as wellformed";
textBoxValidate.Text = "";
}
catch (Exception ex)
{
textBoxLoad.Text = ex.Message;
doc = null;
}
}
private void buttonValidate_Click(object sender, EventArgs e)
{
if (doc == null)
{
textBoxValidate.Text = "No document Loaded";
return;
}
ValidationEventHandler vh=
new ValidationEventHandler(HandleValidateEvents);
try{
validationMsg = "Validated as OK";
doc.Schemas.Add(null, textBoxSchema.Text);
doc.Validate(vh);
textBoxValidate.Text = validationMsg;
}
catch(Exception ex)
{
textBoxValidate.Text = ex.Message;
}
}
// a delegate reporting errors
private void HandleValidateEvents(object ob, ValidationEventArgs args)
{
if (validationMsg.EndsWith("OK"))
validationMsg="";
switch (args.Severity)
{
case XmlSeverityType.Error:
validationMsg += String.Format("\r\nError: {0}", args.Message);
break;
case XmlSeverityType.Warning:
validationMsg += String.Format("\r\nWarning: {0}", args.Message);
break;
}
validationMsg += args.Message + "\r\n";
}
}
}
Du kan lett bytte ut eller endre innholdet i de to filene: results.xml og schema.xsd,
eller du kan ganske enkelt endre adressen i formen.