Linq extraction fichier XML - récupération champ précis

Résolu
cs_JMO Messages postés 1854 Date d'inscription jeudi 23 mai 2002 Statut Membre Dernière intervention 24 juin 2018 - Modifié par cs_JMO le 27/11/2015 à 22:57
Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 - 29 nov. 2015 à 16:57
Bonsoir le forum,

Exemple simplifié du fichier xml:
<?xml version="1.0" encoding="UTF-8"?>
<Customers>
   <Customer name="CLIENT1">
   <Alias name="exploitation" contrat="2435" codebien="I25 Incident Ordonnancement">
      <Parameters>
         <Parameter>
         </Parameter>
      </Parameters>
   </Alias>
   <Alias name="SV_CLIENT1" contrat="5464" codebien="I20 Incident Sauvegarde">
      <Parameters>
         <Parameter>
         </Parameter>
      </Parameters>   
   </Alias>
   </Customer>
   <Customer name="CLIENT2">
      <Alias name="aliasclient2" contrat="1234" codebien="I22 Server Incident">
         <Parameters> 
          <Parameter>ServReboot</Parameter>
          <Parameter>JobReprise</Parameter>
          <Parameter>SQLInstance</Parameter>
          <Parameter>JobSQL</Parameter>  
         </Parameters>
      </Alias> 
   </Customer>
</Customers>  

Via une fonction, je souhaite récupéré la valeur du champ "contrat" d'un client (customer name) et d'un alias (alias name) passés en paramètre.

Mon code ci-dessous me retourne le bon résultat (contrat=5464),
mais je suppose qu'il y a plus adroit pour y arriver.
        Using reader As New IO.StreamReader(pathroot & pathconfig & "TestConfig_forum.xml")
            xdoc = XDocument.Load(reader)
        End Using

        Dim var1 As String = "CLIENT1"
        Dim var2 As String = "SV_CLIENT1"
        Dim var3 As String = "contrat"

        Dim test = From items In xdoc.Descendants("Customer").Elements("Alias").Attributes(var3) _
                   Where CStr(items.Parent.Parent.Attribute("name").Value) = var1 And _
                         CStr(items.Parent.Attribute("name").Value) = var2
                   Select items
       MessageBox.Show(test.First.ToString)


Merci de vos suggestions.

3 réponses

Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656
27 nov. 2015 à 23:04
Bonsoir,
Value est de type string donc tu n'as pas besoin de faire la conversion avec CStr.
Si tu es sûr de n'avoir qu'une occurrence, tu peux utiliser la clause Single au lieu de Where.

Sinon c'est une bonne solution.

Tu peux aussi enchainer 2 requête, la première sélectionne le client, la seconde l'alias.
0
cs_JMO Messages postés 1854 Date d'inscription jeudi 23 mai 2002 Statut Membre Dernière intervention 24 juin 2018 27
27 nov. 2015 à 23:15
Bonsoir Whismeril,

Merci de cette réponse aussi rapide.
Je modifie mon script pour enlever le Cstr superfux et me dirige sur MSDN pour explorer la clause Single que tu me suggères d'employer.

Pour l'occurrence, oui, il y en a une seule, le xml que j'ai créé me sers comme un fichier ini.
J'hésite à lire avec une fonction 1000 fois le fichier d'une centaine de lignes ou de mettre mes données du xml dans un dictionnaire.

Bonne soirée.
0
Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656
28 nov. 2015 à 00:22
Sans hésitation, charge le dans une collection (liste, dictionnaire) et fais tes requêtes directement sur cette collection, ça va beaucoup plus vite.
0
cs_JMO Messages postés 1854 Date d'inscription jeudi 23 mai 2002 Statut Membre Dernière intervention 24 juin 2018 27 > Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024
28 nov. 2015 à 12:06
Bonjour Whismeril,
Merci de tes conseils.

J'ai utilisé un dictionnaire.
' Parcours du fichier XML
Using reader As New IO.StreamReader(pathroot & pathconfig & fileconfig)
xdoc = XDocument.Load(reader)
End Using

Dim job = From items In xdoc.Descendants("Customer") _
Select items

For Each items As XElement In job
Dim customer As String = items.@name
Dim aliascustomer = From itemsalias In items.Descendants("Alias").OrderBy(Function(p) p.Attribute("name").Value)
Select itemsalias

For Each itemscustomer In aliascustomer
Dim parameters = (From itemsparameter In itemscustomer.Descendants("Parameter")
Select itemsparameter.Value).ToList()

dicoCustomer.Add(customer & New String("|"c, 1) & itemscustomer.@name, _
items.@product & New String("|"c, 1) & _
items.@axone & New String("|"c, 1) & _
items.@infocgn & New String("|"c, 1) & _
items.@env & New String("|"c, 1) & _
items.@app & New String("|"c, 1) & _
items.@job & New String("|"c, 1) & _
itemscustomer.@contrat & New String("|"c, 1) & _
itemscustomer.@codebien & New String("|"c, 1) & _
String.Join(New String("|"c, 1), parameters))
Next
Next


Et pour charger ma ListOf
    .CodeContrat = returnTicketing(customer & New Char() {Convert.ToChar(124)} & d.@name, "Contract"), _
.Categorie = returnTicketing(customer & New Char() {Convert.ToChar(124)} & d.@name, "Category")

Private Function returnTicketing(ByVal keyword As String, ByVal choice As String) As String
If dicoCustomer.ContainsKey(keyword) Then
Select Case choice
Case "Contract" : Return dicoCustomer.Item(keyword).ToString.Split(New Char() {Convert.ToChar(124)}, StringSplitOptions.None)(6)
Case "Category" : Return dicoCustomer.Item(keyword).ToString.Split(New Char() {Convert.ToChar(124)}, StringSplitOptions.None)(7)
End Select
End If
Return String.Empty
End Function


Bonne journée
0
Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656
29 nov. 2015 à 14:34
Je pense que tu te complique la vie avec un dictionnaire, son utilité est de lier 2 valeurs, pas 50 compilées.

Il me semble plus judicieux de charger le fichier xml dans une liste d'une classe adaptée et de faire une requête sur cette liste pour alimenter l'autre.
0
Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656
29 nov. 2015 à 15:44
Par exemple, en répondant à ta 1er question, trouver le numéro du contrat en fonction du nom du client et de l'alias

La classe d'abord
Public Class AliasJMO
    Public Property Contrat() As Integer

    Public Property Nom() As String

    Public Property CodeBien() As String

    Public Property Parametres() As List(Of String)

End Class

Public Class Client
    Public Property Nom() As String

    Public Property Aliass() As List(Of AliasJMO)

    Public Shared Function ChargeXML(ByVal Path As String) As List(Of Client)
        Dim xdoc As XDocument = XDocument.Load(Path)

        Return (
            From c In xdoc.Descendants("Customer")
            Select New Client With {.Nom = c.Attribute("name").Value, .Aliass = (
                    From a In c.Descendants("Alias")
                    Select New AliasJMO With {.Nom = a.Attribute("name").Value, .Contrat = Convert.ToInt32(a.Attribute("contrat").Value), .CodeBien = a.Attribute("codebien").Value, .Parametres = (
                            From p In a.Descendants("Parameter")
                            Select p.Value).ToList()}).ToList()}).ToList()
    End Function

End Class


Ensuite la ligne qui demande le chargement
        Dim clients As List(Of Client) = Client.ChargeXML("JMO.xml")


'plus loin la recherche du numéro de contrat
        Dim monContrat As Integer = clients.Single(Function(c) c.Nom = "CLIENT1").Aliass.Single(Function(a) a.Nom = "SV_CLIENT1").Contrat


Pas de split "tordu", on cherche directement la propriété qui va bien.
Pour la requête j'ai considéré que chaque client est unique et qu'un alias est unique par client, sinon on peut combiner Where et First.
0
cs_JMO Messages postés 1854 Date d'inscription jeudi 23 mai 2002 Statut Membre Dernière intervention 24 juin 2018 27
29 nov. 2015 à 15:55
Bonjour Whismeril,

Merci de tes propositions.
En effet, mes split sur les keys et les values du dico ne sont pas très élégants !!!

jean-marc
0
Whismeril Messages postés 19026 Date d'inscription mardi 11 mars 2003 Statut Contributeur Dernière intervention 20 avril 2024 656 > cs_JMO Messages postés 1854 Date d'inscription jeudi 23 mai 2002 Statut Membre Dernière intervention 24 juin 2018
Modifié par Whismeril le 29/11/2015 à 16:57
Ce n'est pas une question d'élégance, mais d'efficacité
  • sérialiser un string pour le spliter juste après, en temps d'exécution ça doit pas être le mieux (quoique parfois c'est surprenant)
  • pas de construction de string pour la recherche, on associe une propriété à une valeur
  • dans 6 mois quand tu reviendras modifier une recherche, tu vas perdre du temps à te demander ou se trouve ton info dans le string sérialité, alors qu'une propriété bien nommé parle de soit
0
Rejoignez-nous