Acces aux valeurs dans un xml [Résolu]

Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
- - Dernière réponse : wholehog2
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
- 18 avril 2019 à 20:39
Bonjour,

Je suis sous vb.net et je cherche a extraire des valeurs d'un fichier xml. J'arrive à récupérer 2 des 3 valeurs que j'ai besoin.
Le xml est de cette forme:

<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="styles/fixture+layer+layers@html@default.xsl"?>
<?xml-stylesheet type="text/xsl" href="styles/fixture+layer+layers@csv.xsl" alternate="yes"?>
<MA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.malighting.de/grandma2/xml/MA" xsi:schemaLocation="http://schemas.malighting.de/grandma2/xml/MA http://schemas.malighting.de/grandma2/xml/3.6.1/MA.xsd" major_vers="3" minor_vers="6" stream_vers="1">
<Info datetime="2019-04-01T09:20:42" showfile="macbeth-test" />
<Layers index="3">
<Layer index="1" name="gradateurs">
<Fixture index="0" name="Dim 68" fixture_id="68" channel_id="68">
<FixtureType name="2 Dimmer 00">
<No>2</No>
</FixtureType>
<SubFixture index="0" react_to_grandmaster="true" color="ffffff">
<Patch>
<Address>143</Address>
</Patch>
<AbsolutePosition>
<Location x="0" y="0" z="0" />
<Rotation x="0" y="-0" z="0" />
<Scaling x="1" y="1" z="1" />
</AbsolutePosition>
<Channel index="0" />
</SubFixture>
</Fixture>


Voila ce que j'ai codé:


Dim cpo_patch As XDocument = XDocument.Load(Lbl_patch.Text)
Dim popatch As XElement = cpo_patch.Root.<Layers>.FirstOrDefault
Dim listPatch As IEnumerable(Of XElement) = popatch.Elements()

For Each listfixture In listPatch

If listfixture.HasElements Then

Dim Fixtures As IEnumerable(Of XElement) = listfixture.Elements

Dim u As Integer = 0

For Each fixture In Fixtures

If fixture.HasElements Then

Tbl_Patch(u, 0) = fixture.@<channel_id>
Tbl_Patch(u, 1) = fixture.<fixturetype>.@<name>
Tbl_Patch(u, 2) = fixture.@<name>

MsgBox(Tbl_Patch(u, 0) & " / " & Tbl_Patch(u, 1) & " / " & Tbl_Patch(u, 2))

End If
Next

End If

Next


ce qui marche pas c'est fixture.<FixtureType>.@<name>

Avez vous des idées sur ce qui ne va pas ?
Afficher la suite 

Votre réponse

20/48 réponses

Messages postés
6003
Date d'inscription
jeudi 13 septembre 2007
Statut
Contributeur
Dernière intervention
18 avril 2019
79
Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354 -
Bonjour le pivert, ton article ne parle pas de la même methode de parsage.
Commenter la réponse de cs_Le Pivert
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Bonjour l’objet XDocument et ses « enfants » XElment et XAttribut ont été conçus pour Linq to Xml, donc pas de la façon dont tu as codé.
Un petit tuto ici
https://codes-sources.commentcamarche.net/faq/11196-linq-to-xml-c-et-vb-net

wholehog2
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
-
merci d'avoir répondu.

J'ai essayé de transformer en fonction du lien que tu m'as donné.
J'ai mis ça mais Tbl_Patch ne fonctionne toujours pas.
 
For Each fixture In Fixtures

        If fixture.HasElements Then

Tbl_Patch(u, 0) = fixture.Attribute("channel_id").Value
Tbl_Patch(u, 1) = fixture.Element("Fixturetype").Attribute("Name").Value
Tbl_Patch(u, 2) = fixture.Attribute("name").Value

MsgBox(Tbl_Patch(u, 0) & "  /  " & Tbl_Patch(u, 1) & "  /  " & Tbl_Patch(u, 2))

         End If
  Next

Suis je dans la bonne direction ?
Commenter la réponse de Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Suis je dans la bonne direction ?


Vu que
  • VB.Net est un langage objet,
  • linq est un système de requetage fait pour travailler avec des collections d'objets,
  • tu utilises un tableau à 2 dimensions
  • tu fais un for each et un If,

franchement non.
En plus tu mélanges VB6 et VB.Net, c'est pas super conseillé (voir ici https://codes-sources.commentcamarche.net/faq/11151-pourquoi-mon-code-vb6-vba-ne-marche-pas-en-vb-net )

Je ne suis pas certain de ce que tu veux faire et comme ton xml est mal formaté (sur le site), je ne peux pas essayer.

Mais si tu as 200 fixtures, alors tu vas avoir 200 messages, et un seul enregistrement (u ne change jamais).


Quand j'étais petit, la mer Morte n'était que malade.
George Burns
wholehog2
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
-
Je te l'accorde, je ne suis pas développeur et j'en suis loin ! , j'ai des notions de vb qui me permettent de faire (quand ça marche) des petits programmes qui me sont utiles.
J'ai volontairement troqué le fichiers xml car effectivement il doit contenir presque 200 fixtures, du coup trop grand pour mettre sur le forum. Voici un lien pour le télécharger :
https://drive.google.com/file/d/1Pb8IVCZGmPP2Usn1KE42Q-700hMnDGAt/view?usp=sharing

Je reformule ce que je souhaite faire au cas ou je me sois mal exprimé.
Je souhaite récupérer les valeurs suivantes pour chaque fixture dans un fichier xml :

<Fixture>.<name>
<Fixture>.<fixture_id>
<Fixture>.<FixtureType>.<name>
p.s.(cette notation est une représentation de ce que je veux récupérer)

pour les mettre ensuite dans un tableau. J'ai ajouté une messagebox pour afficher ce que j'arrivais à récupérer, elle sera évidement supprimé par la suite.

Effectivement j'ai oublié d'incrémenter u dans ma boucle.

Peux tu me donner un exemple de ce que je dois obtenir ?
Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354 -
Ton xml est mal formaté, d'abord parce que tu n'as pas précisé à la coloration que c'est du xml.
Voir ici https://codes-sources.commentcamarche.net/faq/11288-les-balises-de-code
Commenter la réponse de Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Y'a un truc qui ne plait pas à Linq dans ton fichier.

Si j'espionne un fichier de test, j'obtiens ça


Avec le tiens


Je n'ai pas le temps d'y regarder plus avant ce soir.

PS, j'ai codé en C#, mais VB.Net c'est du C# déguisé en VB.
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
Merci de intéresser à mon problème.
Effectivement il y a un souci avec le xml, j'ai vérifié le fichier sur drive il est identique à celui sur mon disque. Peut être devrais tu essayé de la retélécharger au cas ou.
De mon coté je vérifie demain ce que ça donne sur visual studio.

p.s. j'ai cherché comment éditer mon premier post pour passer le code en xml mais je n'ai pas trouvé
Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354 -
Bonjour, on peut éditer son propre post pendant 1h à partir du dépôt.
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Apparement, c'est les lignes 2 à 4 qui ne plaisent pas à Linq (et je ne sais pas pourquoi), si on fait le ménage de cette façon (à la main pour l'instant)
<?xml version="1.0" encoding="utf-8"?>
<MA>
	<Info datetime="2019-04-01T09:20:42" showfile="macbeth limoges-test" />
	<Layers index="3">
		<Layer index="1" name="gradateurs">
			<Fixture index="0" name="Dim 68" fixture_id="68" channel_id="68">
				<FixtureType name="2 Dimmer 00">
					<No>2</No>
				</FixtureType>
				<SubFixture index="0" react_to_grandmaster="true" color="ffffff">
					<Patch>
						<Address>143</Address>
					</Patch>
					<AbsolutePosition>
						<Location x="0" y="0" z="0" />
						<Rotation x="0" y="-0" z="0" />
						<Scaling x="1" y="1" z="1" />
					</AbsolutePosition>
					<Channel index="0" />
				</SubFixture>
			</Fixture>


ça marche.

La classe qui va recevoir les données
Class Fixture
		Public Property Nom() As String

		Public Property Type() As String

		Public Property NomType() As String

	End Class


Je suppose que tu es en Winform et pour l'exemple il y a un datagridview sur le form.
Le code est lancé depuis un bouton.
		Private mesFixtures As List(Of Fixture) 'collection qui va recevoir les data
		Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
			Dim xDoc As XDocument = XDocument.Load("PATCH2.xml") 'dans cet exemple le fichier est placé dans le même répertoire que le logiciel
			mesFixtures = (
			    From f In xDoc.Descendants("Fixture")'f est un instance d'élément appelé "Fixture"
			    Select New Fixture With
                            {
                                   .Nom = f.Attribute("name")?.Value, ' les ? veulent dire si c'est pas null alors donne moi la suite sinon retourne null,
                                   .Type = f.Value,
                                   .NomType = f.Element("FixtureType")?.Attribute("name")?.Value
                           }).ToList() 

			'on binde les résultats vers un datagridview, en supposant que tu es en Winform
			dataGridView1.DataSource = mesFixtures
		End Sub
   }


Attention, enlève les commentaires (qui sont là pour t'expliquer) de la requête, sinon ça ne marchera pas.


Autre chose, on ne déboggue pas avec des messagesBox, il y a des outils exprès pour
https://openclassrooms.com/fr/courses/1526901-apprenez-a-developper-en-c/2867766-utilisez-le-debogueur/
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
Oui je suis en winform.

Effectivement en enlevant les lignes 2 et 3 ça marche. Ces lignes servent à la présentation du fichier xml avec une feuille de style xsl.
Du coup est-ce que je dois modifier le xml avant de le lire ? ou y a t'il une autre solution ? (idée ; définir la racine du xml sur le nœud <layers>)

ok pour le débogage mais je trouvais que c'était plus facile comme ça.
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
ok pour le débogage mais je trouvais que c'était plus facile comme ça.

Et non, avec un point d’arrêt et les outils, tu tout voir.
Et puis une fois que tu as compris ce qui ne va pas tu enlève le point d’arrêt et t’as pas à cliquer sur 131 messagebox....

Pour ton fichier, je pense que le plus simple est effectivement de le modifier.
Je te montre ça plus tard dans la journée.
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
Oui c'est vrai que ça fait beaucoup appuyer sur la touche "entrer", je vais m'habituer avec les outils de débogage de visual studio se sera mieux.

En attendant ta solution pour le fichier, je vais voir ce que j'arrive à faire.
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
        Dim lignes As List(Of String) = File.ReadAllLines("PATCH.xml").ToList() 'on met toutes les lignes du fichier dans une lite
        lignes.RemoveAt(1) 'on enlève la ligne 1 (qui est la 2eme ligne)
        lignes.RemoveAt(1) 'on enlève la ligne 1 (qui est la 2eme ligne actuelle, soit la 3ème ancienne ligne)
        lignes(1) = "<MA>" 'on modifie la ligne 1 (qui est la 4eme ligne d'origine)

        Dim texteCorrige As String = String.Join(Environment.NewLine, lignes) 'on refait un texte complet

        Dim xDoc As XDocument = XDocument.Parse(texteCorrige) 'on parse le texte corrigé
        mesFixtures = (
                From f In xDoc.Descendants("Fixture")
                Select New Fixture With
                 {
                    .Nom = f.Attribute("name")?.Value,
                    .Type = f.Value,
                    .NomType = f.Element("FixtureType")?.Attribute("name")?.Value
                 }).ToList() 

        DataGridView1.DataSource = mesFixtures

Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
super, merci encore ça fonctionne comme je voulais.

Une petite question en bonus :
si dans mon xml j'avais voulu récupérer les valeurs de <fixture><subfixture><absoluteposition><location><X> comment aurais je du procéder ?
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Ça dépend de comment tu dois ensuite le traiter, l'utiliser.
  • est-ce dépendant du fixture?
    • oui, doit-être dans l'objet fixture
      • dans le xml, c'est un "sous ensemble", est-ce le cas pour le traitement
        • non, propriété de type de base
        • oui, propriété d'une classe dédiée
      • peut-il y en avoir plusieurs dans un fixture
        • oui
          • nombre fixe
          • nombre variable
    • non, doit-être dans un autre objet-> classe dédié


Par exemple, un carnet de contact (pour les collections)
La classe contact a pour propriété nom, prénom, surnom, date de naissance, société.
Arrivent les numéros de téléphone,
  • est-ce que je crée pleins de propriétés fixeMaison, fixeBureau, portableMaison, portableBureau, faxBureau, télex, bippeur, etc....
    • il faut être super exhaustif (c'est compliqué, et on va se retrouver avec "50" propriétés quasi identiques) et on n'autorise pas l'avenir (un codeur de carnet de contact des années 80 avait il anticipé les smartphone double sim?)
  • ou alors est-ce que je crée une classe "Téléphone" avec une propriété numéro, une propriété device (fixe, mobile, bippeur, fax, etc..) et une propriété type (perso, bureau, voiture etc...).
    • Là on dispose d'une collection, dont on peut moduler les enregistrement "à l'infini"

Et le problème se repose pour les adresses postales, les adresse mails, les compte de réseau sociaux etc...

Autre exemple (pour un sous ensemble unique)
On code une voiture, l'objet conducteur existe déjà, il sait qu'il doit démarrer, enclencher une vitesse, et ensuite combiner accélération et action sur le volant.
La voiture doit donc disposer de toutes ces "actionneurs".
Voyons démarrer, cela implique, la batterie et le moteur. Mais est-ce à l'objet Voiture de savoir qu'il va falloir pomper du carburant, l'injecter, déclencher une étincelle, qui provoquera une explosion qui mettra en mouvement le piston, qui lui-même mettra en rotation le vilebrequin. Ou la voiture doit-elle se contenter de mettre du courant au moteur et lui se gère de son coté?
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
C'est les SubFixtures qui créent le fixture donc il peut y en avoir un ou plusieurs(nombre variable) donc ils leurs faut une classe dédiée.

On crée une classe pour le ou les subfixtures
mais après :
 From f In xDoc.Descendants("Fixture")'f est un instance d'élément appelé "Fixture"
			    Select New Fixture With
                            {
                                   .Nom = f.Attribute("name")?.Value, ' les ? veulent dire si c'est pas null alors donne moi la suite sinon retourne null,
                                   .Type = f.Value,
                                   .NomType = f.Element("FixtureType")?.Attribute("name")?.Value
                           }).ToList() 

Il faut faire quoi?
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
On commence par 2 nouvelles classes

	Class SubFixture
		Public Property PatchAdresse() As Integer

		Public Property Position() As Point3D

		Public Property Rotation() As Point3D

		Public Property Echelle() As Point3D
	End Class

	Friend Class Point3D
		Public Property X() As Double

		Public Property Y() As Double

		Public Property Z() As Double

		''' <summary>
		''' Cette méthode évite d'écrire 3fois la même chose dans la requete de désserialisation
		''' </summary>
		''' <param name="E"></param>
		''' <returns></returns>
		Public Shared Function FromXML(ByVal E As XElement) As Point3D
			If E Is Nothing Then
				Return New Point3D()
			End If

			Return New Point3D With {.X = ToDouble(E.Attribute("x")?.Value), .Y = ToDouble(E.Attribute("y")?.Value), .Z = ToDouble(E.Attribute("z")?.Value)}
		End Function

		''' <summary>
		''' convertit en double peu importe le symbole décimal du PC et du texte
		''' </summary>
		''' <param name="Texte"></param>
		''' <returns>Si ca ne marche pas retourne Not A Number</returns>
		Private Shared Function ToDouble(ByVal Texte As String) As Double
			Dim res As Double

			If Double.TryParse(Texte.Replace(","c, "."c),System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, res) Then
				Return res
			Else
				Return Double.NaN
			End If
		End Function
	End Class

La classe Point3D possède une méthode Shared qui construit une instance à partir de l'élément XML qui va bien.
Cette méthode doit convertir 3 doubles, écrits avec des points et mon pc est en virgule, alors elle appelle une autre méthode qui gère le problème, cette seconde méthode est Shared pour être utilisable depuis l'autre.


On modifie un peu la requête d'import
			mesFixtures = (
			    From f In xDoc.Descendants("Fixture")
			    Select New Fixture With {.Nom = f.Attribute("name")?.Value, .Type = f.Value, .NomType = f.Element("FixtureType")?.Attribute("name")?.Value, .LesSubFixtures = (
			            From s In f.Descendants("SubFixture")
			            Select New SubFixture With {
                                           .PatchAdresse = s.Element("Patch")?.Element("Address")?.Value IsNot Nothing ? Convert.ToInt32(s.Element("Patch")?.Element("Address")?.Value) : -1, 
                                           .Position = Point3D.FromXML(s.Element("AbsolutePosition")?.Element("Location")),
                                           .Rotation = Point3D.FromXML(s.Element("AbsolutePosition")?.Element("Rotation")),
                                           .Echelle = Point3D.FromXML(s.Element("AbsolutePosition")?.Element("Scaling"))}).ToList()}).ToList()


On peut même s'amuser à compter combien de fixtures ont plusieurs subfixtures, ou combien n'en n'ont pas
			Dim n1 As Integer = mesFixtures.Count(Function(f) f.LesSubFixtures.Count > 1)
			Dim n0 As Integer = mesFixtures.Count(Function(f) f.LesSubFixtures.Count = 0)

ou alors lister tous les fixtures dont au moins un subfixture n'a pas d'adresse de patch
Dim pbImport As List(Of Fixture) = mesFixtures.Where(Function(f) f.LesSubFixtures.Any(Function(s) s.PatchAdresse = -1)).ToList()

Dans ce fichier, les 3 requêtes donnent 0.

Whismeril
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354 -
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
Génial merci beaucoup, j'ai tout ce qu'il me faut
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
De rien
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
j'aurais encore besoin de ton aide, si tu es d'accord.
J'ai essayé de mettre en application ton avant-dernier post mais j'ai des soucis.
Je travaille maintenant avec un autre fichier xml plus compliqué(avec environ 17000 lignes) que voici :
https://drive.google.com/file/d/1EUJgjJZv8tvtvvTC9SEyIeqg8IQHZBxZ/view?usp=sharing
Il faut le modifier pour que Linq puisse l'interpréter:
Il faut :
- remplacer la ligne 2 par "<MA>"
- supprimer toutes les lignes qui contiennent "<Preset xsi:nil="True" />"

Jusque là ça va!

Ensuite j'ai crée des class pour accueillir les données comme ceci :
Class Presets
    Public Property Name() As Integer
    Public Property SpecialUse() As String
    Public Property Channels() As Channels
End Class

Class Channels
    Public Property PresetValue As PresetValue
    Public Property Channel As Channel
End Class

Class PresetValue
    Public Property Handle() As Handle ' quand il y a le noeud HANDLE c'est qu'il y a une preset embeded
    Public Property Channel() As Channel
End Class

Class Handle
    Public Property Name() As Integer
    Public Property Numero() As Integer
End Class

Class Channel
    Public Property Fixture_id As Integer
    Public Property Attribut As String
End Class


J'ai commencé à écrire la ligne pour récupérer les infos mais ça ne fonctionne pas.


Je n'ai pas fini de taper la ligne mais peux tu me dire ce qui ne va pas.
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Rapidement, sans avoir testé.
New Trucmuche With
{'avec une accolade


Autre chose Handle existe déjà en .Net, il serait judicieux d'appeler la classe autrement ou à défaut de la mettre dans un manespace.
Quand j'étais petit, la mer Morte n'était que malade.
George Burns
Commenter la réponse de Whismeril
Messages postés
30
Date d'inscription
lundi 25 juin 2007
Statut
Membre
Dernière intervention
18 avril 2019
0
Merci
J'ai fait des corrections :
j'ai renommer Handle par Embeded
et changer la ligne :

Mais Il y a encore des erreurs
Commenter la réponse de wholehog2
Messages postés
13121
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
18 avril 2019
354
0
Merci
Peux tu poster le code?
Commenter la réponse de Whismeril

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.