Linq extraction fichier XML - récupération champ précis [Résolu]

cs_JMO 1850 Messages postés jeudi 23 mai 2002Date d'inscription 11 mai 2018 Dernière intervention - 27 nov. 2015 à 22:48 - Dernière réponse : Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention
- 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.
Afficher la suite 

Votre réponse

8 réponses

Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention - 27 nov. 2015 à 23:04
0
Utile
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.
cs_JMO 1850 Messages postés jeudi 23 mai 2002Date d'inscription 11 mai 2018 Dernière intervention - 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.
Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention - 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.
cs_JMO 1850 Messages postés jeudi 23 mai 2002Date d'inscription 11 mai 2018 Dernière intervention > Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention - 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
Commenter la réponse de Whismeril
Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention - 29 nov. 2015 à 14:34
0
Utile
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.
Commenter la réponse de Whismeril
Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention - 29 nov. 2015 à 15:44
0
Utile
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.
cs_JMO 1850 Messages postés jeudi 23 mai 2002Date d'inscription 11 mai 2018 Dernière intervention - 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
Whismeril 11529 Messages postés mardi 11 mars 2003Date d'inscriptionContributeurStatut 21 mai 2018 Dernière intervention > cs_JMO 1850 Messages postés jeudi 23 mai 2002Date d'inscription 11 mai 2018 Dernière intervention - 29 nov. 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
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.