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:
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
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
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 ?
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é
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)
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.
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.
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.
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
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 ?
Ç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é?
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()
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()
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.
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
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.
Suis je dans la bonne direction ?