FAQ C++/CLI et VC++.NetConsultez toutes les FAQ

Nombre d'auteurs : 29, nombre de questions : 248, création le 22 février 2013 

 
OuvrirSommaireIntéraction du C++/CLI avec le framework .NetFichiers, Répertoires, DisquesXML

Voici un exemple basique de lecture d'un fichier Xml en se servant non pas de la classe XmlDocument mais des classes de l'espace de noms System::Xml::Xpath. Deux cas sont distingués, les fichiers Xml de base et ceux avec un espace de noms.

Fichier XML basique
Sélectionnez

<Recordbook>
  <Records>
    <Record>
      <FirstValue>10</FirstValue>
      <SecondValue>51</SecondValue>
    </Record>
    <Record>
      <FirstValue>25</FirstValue>
      <SecondValue>38</SecondValue>
    </Record>
  </Records>
</Recordbook>
      
son implémentation
Sélectionnez

using namespace System::Xml::XPath;
 
void TraiteXml(String ^ fileName)
{
	XPathDocument ^ doc = gcnew XPathDocument(fileName);
    XPathNavigator ^ nav = doc->CreateNavigator();
    // On récupère un XPathNodeIterator sur les Record
    XPathNodeIterator ^ iter = nav->Select("Recordbook/Records/Record");
    // Pour chaque Record
    while (iter->MoveNext())
    {
        // On récupère l'info FirstValue
        String ^ firstValue = iter->Current->SelectSingleNode("FirstValue")->Value;
        // On récupère l'info SecondValue
        String ^ secondValue = iter->Current->SelectSingleNode("SecondValue")->Value;
		Console::WriteLine("{0},{1}", firstValue, secondValue);
    }
}
Fichier Xml avec un espace de noms
Sélectionnez

<rd:Recordbook xmlns:rd="http://myexemple/myschema/record">
  <rd:Records>
    <rd:Record>
      <rd:FirstValue>10</rd:FirstValue>
      <rd:SecondValue>51</rd:SecondValue>
    </rd:Record>
    <rd:Record>
      <rd:FirstValue>25</rd:FirstValue>
      <rd:SecondValue>38</rd:SecondValue>
    </rd:Record>
  </rd:Records>
</rd:Recordbook>
      
son implémentation
Sélectionnez

using namespace System::Xml;
 
void TraiteXmlEspaceNom(String ^ fileName)
{
    XPathDocument ^ doc = gcnew XPathDocument(fileName);
    XPathNavigator ^ nav = doc->CreateNavigator();
    // On ajoute la gestion des espaces de noms
    XmlNamespaceManager ^ mgr = gcnew XmlNamespaceManager(nav->NameTable);
    mgr->AddNamespace("rd", "http://myexemple/myschema/record");
    // On récupère un XPathNodeIterator sur les Record
    XPathNodeIterator ^ iter = nav->Select("rd:Recordbook/rd:Records/rd:Record", mgr);
    // Pour chaque Record
    while (iter->MoveNext())
    {
        // On récupère l'info FirstValue
        String ^ firstValue = iter->Current->SelectSingleNode("FirstValue", mgr)->Value;
        // On récupère l'info SecondValue
        String ^ secondValue = iter->Current->SelectSingleNode("SecondValue", mgr)->Value;
		Console::WriteLine("{0},{1}", firstValue, secondValue);
    }
}

Remarque :
Attention, XPath est sensible à la casse. Le nom des balises doit correspondre exactement.

 
Sélectionnez

nav->Select("Recordbook/Records/Record");

et

 
Sélectionnez

nav->Select("Recordbook/Records/record");

ne représentent pas la même chose.

Créé le 27 mars 2007  par StormimOn, nico-pyright(c)

Précédemment, nous avons vu comment lire un fichier avec les classes XPath. Dans le cas de la présence de namespace dans le fichier Xml, nous avons utilisé la classe XmlNamespaceManager pour gérer ces espaces de noms. Le seul défaut c'est que nous alimentions manuellement ces données, nous allons donc maintenant créer ce XmlNamespaceManager de manière automatique.

Fichier Xml avec un espace de noms
Sélectionnez

<rd:Recordbook xmlns:rd="http://myexemple/myschema/record">
  <rd:Records>
    <rd:Record>
      <rd:FirstValue>10</rd:FirstValue>
      <rd:SecondValue>51</rd:SecondValue>
    </rd:Record>
    <rd:Record>
      <rd:FirstValue>25</rd:FirstValue>
      <rd:SecondValue>38</rd:SecondValue>
    </rd:Record>
  </rd:Records>
</rd:Recordbook>
      
 
Sélectionnez

using namespace System::Collections::Generic;
using namespace System::Xml;
using namespace System::Xml::XPath;
 
XmlNamespaceManager ^ GetXmlNamespaceManager(XPathNavigator ^ nav)
{            
    XmlNamespaceManager ^ mgr = nullptr;
    nav->MoveToFirstChild();
    for each (KeyValuePair<String ^, String ^> ^ keyPair in nav->GetNamespacesInScope(XmlNamespaceScope::Local))
    {
        if (mgr == nullptr)
			mgr = gcnew XmlNamespaceManager(nav->NameTable); 
        mgr->AddNamespace(keyPair->Key, keyPair->Value);
    }
    nav->MoveToRoot();
    return mgr;
}
 
void TraiteXml(String ^ fileName)
{
    XPathDocument ^ doc = gcnew XPathDocument(fileName);
    XPathNavigator ^ nav = doc->CreateNavigator();
    XmlNamespaceManager ^ mgr = GetXmlNamespaceManager(nav);            
    if (mgr != nullptr)
    {
        XPathNodeIterator ^ iter = nav->Select("rd:Recordbook/rd:Records/rd:Record", mgr);
        while (iter->MoveNext())
		{
			String ^ firstValue = iter->Current->SelectSingleNode("rd:FirstValue", mgr)->Value;
            String ^ secondValue = iter->Current->SelectSingleNode("rd:SecondValue", mgr)->Value;
			Console::WriteLine("{0},{1}", firstValue, secondValue);
        }
    }
}
      
Créé le 27 mars 2007  par StormimOn, nico-pyright(c)

Si vous devez valider un fichier XML avec un schéma XSD, voici la marche à suivre.

Fichier Xml exemple (books.xml)
Sélectionnez

<?xml version="1.0"?>
<catalog>
	<!--
	<title>2000-10-01</title>
	-->
	<book id="bk101">
		<author>Gambardella, Matthew</author>
		<title>XML Developer's Guide</title>
		<genre>Computer</genre>
		<prices>44.95</price>
		<publish_date>2000-10-01</publish_dates>
		<description>An in-depth look at creating applications with  XML.</description>
	</book>
</catalog>
      
Fichier XSD exemple (books.xsd)
Sélectionnez

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
	<xsd:element name="catalog" type="CatalogData"/>
	<xsd:complexType name="CatalogData">
		<xsd:sequence>
			<xsd:element name="book" type="bookdata" minOccurs="0" maxOccurs="unbounded"/>
		</xsd:sequence>
	</xsd:complexType>
	<xsd:complexType name="bookdata">
		<xsd:sequence>
			<xsd:element name="author" type="xsd:string"/>
			<xsd:element name="title" type="xsd:string"/>
			<xsd:element name="genre" type="xsd:string"/>
			<xsd:element name="price" type="xsd:float"/>
			<xsd:element name="publish_date" type="xsd:date"/>
			<xsd:element name="description" type="xsd:string"/>
		</xsd:sequence>
		<xsd:attribute name="id" type="xsd:string"/>
	</xsd:complexType>
</xsd:schema>
      
 
Sélectionnez

using namespace System::Xml;
using namespace System::Xml::Schema;
 
public ref class ValidXml
{
private:
	static bool error;
	static void XmlValidationError(Object ^ sender, ValidationEventArgs ^ e)
	{
		// code à exécuter si erreur à la validation du Xml suivant le schéma Xsd
		// on passe autant de fois dans ce code qu'il y a d'erreurs
		Debug::WriteLine(e->Exception->Message);
		error = true;
	}
public:
	static Boolean isXmlValide(String ^xsdFile, String ^xmlFile)
	{
		error = false;
		try
		{
			XmlReaderSettings ^ settings = gcnew XmlReaderSettings();
			settings->Schemas->Add(nullptr, xsdFile);
			settings->ValidationType = ValidationType::Schema;
			settings->ValidationEventHandler += gcnew ValidationEventHandler(XmlValidationError);
			XmlReader ^ reader = XmlReader::Create(xmlFile, settings);
			while (reader->Read()) ;
		} catch (Exception ^)
		{
			return false;
		}
		return !error;
	}
 
};
 
void main () // test
{
	Console::WriteLine(ValidXml::isXmlValide("c:\\books.xsd", "c:\\books.xml"));
 
}
      
Créé le 27 mars 2007  par StormimOn, nico-pyright(c)

On utlise l'objet XmlSerializer pour effectuer une sérialisation et une désérialisation en xml.
Soit l'objet suivant :

 
Sélectionnez
public ref class Personne
{
private:
	String ^_login;
	String ^_pwd;
	int _age;
public:
	Personne(){}
	Personne(String ^login, String ^pwd, int age) : _login(login), _pwd(pwd), _age(age) {}
 
	property String ^Login
	{
		String ^ get() { return _login; }
		void set(String ^value) { _login = value; }
	}
	[System::Xml::Serialization::XmlIgnore()]
	property String ^Pwd
	{
		String^ get() { return _pwd; }
		void set(String ^value) { _pwd = value; }
	}
	property int Age
	{
		int get() { return _age; }
		void set(int value) { _age = value; }
	}
 
	virtual String^ ToString() override
	{
		return String::Format("Login : {0} - Mot de passe : {1} - Age : {2}", _login, _pwd, _age);
	}
};

Je peux choisir délibérément de ne pas sérialiser certaines propriétés. Par exemple, la propriété Pwd ne sera pas sérialisée grâce à l'attribut XmlIgnore.
NB : Pour être sérialisable, une classe doit-être publique, les membres à sérialiser doivent l'être également. La classe devra également posséder un constructeur par défaut et une propriété set pour chaque propriété get.

On effectue la sérialisation ainsi :

 
Sélectionnez

	Personne ^moi = gcnew Personne("Nico", "Abcd", 28);
	System::Xml::Serialization::XmlSerializer ^ sr = gcnew System::Xml::Serialization::XmlSerializer(Personne::typeid);
	System::IO::StreamWriter ^ writer = gcnew System::IO::StreamWriter("fichier.xml");
	try
	{
		sr->Serialize(writer, moi);
	}
	catch (Exception ^ e)
	{
		Console::WriteLine("Impossible de sérialiser : " + e->Message);
	}
	finally
	{
		writer->Close();
	}

On utilise ici directement un StreamWriter pour enregistrer un fichier xml, mais il est envisageable d'utiliser d'autres surcharges, pour par exemple obtenir une chaine.

La désérialisation se déroule ainsi :

 
Sélectionnez

	Personne ^moiClone;
	System::IO::StreamReader ^reader = gcnew System::IO::StreamReader("fichier.xml");
	try
	{
		moiClone = (Personne^)sr->Deserialize(reader);
		Console::WriteLine(moiClone->ToString());
	}
	catch (Exception ^ e)
	{
		Console::WriteLine("Impossible de désérialiser : " + e->Message);
	}
	finally
	{
		reader->Close();
	}

Notez bien sur que l'appel à ToString restitue une chaine vide pour le mot de passe car cette propriété n'a pas été sérialisée.

Créé le 20 novembre 2007  par nico-pyright(c)

Prenons le cas de deux classes, classA et classB :

 
Sélectionnez
ref class classB;
 
[Serializable()]
public ref class classA
{
private:
	int m_MembreInt;
	classB ^m_B;
public:
	property int MembreInt;
	property classB ^B;
};
 
[Serializable()]
public ref class classB
{
private:
	float m_MembreFloat;
	classA ^m_A;
public:
	property float MembreFloat;
	property classA ^A;
};

Comme vous pouvez le voir, classA a une référence vers classB et classB a une référence vers classA. Si nous essayons de sérialiser un objet de type classA ou classB (et à condition que les références correspondantes ne soient pas nulles), nous aurons une erreur de référence circulaire et la sérialisation s'avèrera impossible :

 
Sélectionnez
classA ^objA = gcnew classA();
classB ^objB = gcnew classB();
 
objA->B = objB;
objB->A = objA;
 
// sérialisation
System::Xml::Serialization::XmlSerializer ^ sr = gcnew System::Xml::Serialization::XmlSerializer(classA::typeid);
System::IO::StreamWriter ^ writer = gcnew System::IO::StreamWriter("fichier.xml");
sr->Serialize(writer, objA);
writer->Close();

Le code ci dessous génèrera le message d'erreur suivant à l'exécution :

 
Sélectionnez
Référence circulaire détectée lors de la sérialisation d'un objet de type ConsoleApplication2.classA.

Afin de retirer les références circulaires, il suffira d'exclure les objets en question de la sérialisation en utilisant l'attribut System.Xml.Serialization.XmlIgnore sur les propriétés concernées :

 
Sélectionnez
[System::Xml::Serialization::XmlIgnore()]
property classB ^B;

et

 
Sélectionnez
[System::Xml::Serialization::XmlIgnore()]
property classA ^A;
Créé le 9 octobre 2008  par Jérôme Lambert, nico-pyright(c)
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006-2007 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.