Acces aux valeurs dans un xml

Résolu
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019 - 4 avril 2019 à 12:00
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 - 17 mai 2019 à 20:21
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 ?

88 réponses

Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
4 avril 2019 à 13:21
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

1
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
4 avril 2019 à 15:58
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 ?
0
cs_Le Pivert Messages postés 7903 Date d'inscription jeudi 13 septembre 2007 Statut Contributeur Dernière intervention 11 mars 2024 137
4 avril 2019 à 12:56
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
4 avril 2019 à 13:14
Bonjour le pivert, ton article ne parle pas de la même methode de parsage.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
Modifié le 4 avril 2019 à 19:02
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).


0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
5 avril 2019 à 14:27
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 ?
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
5 avril 2019 à 18:22
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
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
5 avril 2019 à 18:57
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.
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
5 avril 2019 à 21:44
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é
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 07:36
Bonjour, on peut éditer son propre post pendant 1h à partir du dépôt.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 08:46
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/
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
6 avril 2019 à 09:57
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.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 10:31
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.
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
6 avril 2019 à 10:44
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.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 13:16
        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

0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
6 avril 2019 à 15:29
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 ?
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 16:30
Ç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é?
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
6 avril 2019 à 16:55
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?
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 22:09
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.

0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
6 avril 2019 à 22:11
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
6 avril 2019 à 22:46
Génial merci beaucoup, j'ai tout ce qu'il me faut
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
7 avril 2019 à 00:46
De rien
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
Modifié le 8 avril 2019 à 16:26
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.
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
Modifié le 8 avril 2019 à 18:25
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.
0
wholehog2 Messages postés 58 Date d'inscription lundi 25 juin 2007 Statut Membre Dernière intervention 17 mai 2019
8 avril 2019 à 20:42
J'ai fait des corrections :
j'ai renommer Handle par Embeded
et changer la ligne :

Mais Il y a encore des erreurs
0
Whismeril Messages postés 19028 Date d'inscription mardi 11 mars 2003 Statut Non membre Dernière intervention 24 avril 2024 656
8 avril 2019 à 20:49
Peux tu poster le code?
0
Rejoignez-nous