2011-06-21 20 views
5

Parece que tengo un problema con la recuperación de valores XML con C#, que sé que se debe a mi conocimiento muy limitado de C# y .XML.Recuperación de datos del archivo XML

se me dio el siguiente archivo XML

<PowerBuilderRunTimes> 
    <PowerBuilderRunTime> 
     <Version>12</Version> 
     <Files> 
      <File>EasySoap110.dll</File> 
      <File>exPat110.dll</File> 
      <File>pbacc110.dll</File> 
     </File> 
    </PowerBuilderRunTime> 
</PowerBuilderRunTimes> 

estoy para procesar el archivo XML y asegurarse de que cada uno de los archivos en el existen en la carpeta (que es la parte fácil). Es el procesamiento del archivo XML con el que tengo dificultades. Esto es lo que he hecho hasta ahora:

var runtimeXml = File.ReadAllText(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes)); 

var doc = XDocument.Parse(runtimeXml); 
var topElement = doc.Element("PowerBuilderRunTimes"); 
var elements = topElement.Elements("PowerBuilderRunTime"); 

foreach (XElement section in elements) 
{ 
    //pbVersion is grabbed earlier. It is the version of PowerBuilder 
    if(section.Element("Version").Value.Equals(string.Format("{0}", pbVersion))) 
    { 
     var files = section.Elements("Files"); 

     var fileList = new List<string>(); 

     foreach (XElement area in files) 
     { 
      fileList.Add(area.Element("File").Value); 
     } 
    } 
} 

Mi problema es que la lista de cadenas sólo es cada vez rellena con un valor, "EasySoap110.dll", y todo lo demás se ignoran. ¿Puede alguien ayudarme, ya que estoy perdido?

+0

No comience su asunto con "C#". Para eso son las etiquetas. –

Respuesta

10

mirada a este bit:

var files = section.Elements("Files"); 

var fileList = new List<string>(); 

foreach (XElement area in files) 
{ 
    fileList.Add(area.Element("File").Value); 
} 

Estás iterar sobre cada elemento Files, y luego encontrar el primer elemento de File dentro de ella. Solo hay un elemento Files; debe iterar sobre los elementos File dentro de ese.

Sin embargo, definitivamente hay mejores formas de hacerlo. Por ejemplo:

var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes)); 
var fileList = (from runtime in doc.Root.Elements("PowerBuilderRunTime") 
       where (int) runtime.Element("Version") == pbVersion 
       from file in runtime.Element("Files").Elements("File") 
       select file.Value) 
       .ToList(); 

Tenga en cuenta que si hay múltiples juego PowerBuilderRunTime elementos, que va a crear una lista con todos los los archivos de todos los esos elementos. Eso puede no ser lo que quieres. Por ejemplo, es posible que desee:

var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes)); 
var runtime = doc.Root 
       .Elements("PowerBuilderRunTime") 
       .Where(r => (int) r.Element("Version") == pbVersion) 
       .Single(); 

var fileList = runtime.Element("Files") 
         .Elements("File") 
         .Select(x => x.Value) 
         .ToList(); 

que validará que no es exactamente una tiempo de ejecución coincidente.

+1

¡Impresionante, eso es exactamente lo que estaba buscando! ¡Gracias por ayudarme a entender! –

-1

Debería intentar reemplazar este material con una simple consulta XPath.

 string configPath; 
     System.Xml.XPath.XPathDocument xpd = new System.Xml.XPath.XPathDocument(cofigPath); 
     System.Xml.XPath.XPathNavigator xpn = xpd.CreateNavigator(); 
     System.Xml.XPath.XPathExpression exp = xpn.Compile(@"/PowerBuilderRunTimes/PwerBuilderRunTime/Files//File"); 
     System.Xml.XPath.XPathNodeIterator iterator = xpn.Select(exp); 
     while (iterator.MoveNext()) 
     { 
      System.Xml.XPath.XPathNavigator nav2 = iterator.Current.Clone(); 
      //access value with nav2.value 
     } 
+0

Sin enlace ni explicación adicional, puede ser difícil de seguir. –

2

El problema es que solo hay un elemento en su XML, con varios elementos secundarios. El bucle foreach solo se ejecuta una vez, para el elemento individual, no para sus elementos secundarios.

hacer algo como esto:

var fileSet = files.Elements("File"); 
foreach (var file in fileSet) { 
    fileList.Add(file.Value); 
} 

el que los bucles sobre todos los elementos infantiles.

0

Siempre he preferido usar lectores para leer archivos de configuración XML de cosecha propia. Si solo haces esto una vez, es probable que se haya acabado, pero los lectores son más rápidos y más baratos.

public static class PowerBuilderConfigParser 
{ 
    public static IList<PowerBuilderConfig> ReadConfigFile(String path) 
    { 
     IList<PowerBuilderConfig> configs = new List<PowerBuilderConfig>(); 
     using (FileStream stream = new FileStream(path, FileMode.Open)) 
     { 
      XmlReader reader = XmlReader.Create(stream); 
      reader.ReadToDescendant("PowerBuilderRunTime"); 
      do 
      { 
       PowerBuilderConfig config = new PowerBuilderConfig(); 
       ReadVersionNumber(config, reader); 
       ReadFiles(config, reader); 
       configs.Add(config); 
       reader.ReadToNextSibling("PowerBuilderRunTime"); 
      } while (reader.ReadToNextSibling("PowerBuilderRunTime")); 
     } 
     return configs; 
    } 

    private static void ReadVersionNumber(PowerBuilderConfig config, XmlReader reader) 
    { 

     reader.ReadToDescendant("Version"); 
     string version = reader.ReadString(); 

     Int32 versionNumber; 
     if (Int32.TryParse(version, out versionNumber)) 
     { 
      config.Version = versionNumber; 
     } 
    } 

    private static void ReadFiles(PowerBuilderConfig config, XmlReader reader) 
    { 
     reader.ReadToNextSibling("Files"); 
     reader.ReadToDescendant("File"); 
     do 
     { 
      string file = reader.ReadString(); 
      if (!string.IsNullOrEmpty(file)) 
      { 
       config.AddConfigFile(file); 
      } 
     } while (reader.ReadToNextSibling("File")); 
    } 
} 

public class PowerBuilderConfig 
{ 
    private Int32 _version; 
    private readonly IList<String> _files; 

    public PowerBuilderConfig() 
    { 
     _files = new List<string>(); 
    } 

    public Int32 Version 
    { 
     get { return _version; } 
     set { _version = value; } 
    } 

    public ReadOnlyCollection<String> Files 
    { 
     get { return new ReadOnlyCollection<String>(_files); } 
    } 

    public void AddConfigFile(String fileName) 
    { 
     _files.Add(fileName); 
    } 
} 
+0

También para el OP, su XML no está bien formado, su comienzo termina con. –

0

Otra forma es utilizar un XmlSerializer.

[Serializable] 
[XmlRoot] 
public class PowerBuilderRunTime 
{ 
[XmlElement] 
public string Version {get;set;} 
[XmlArrayItem("File")] 
public string[] Files {get;set;} 

public static PowerBuilderRunTime[] Load(string fileName) 
{ 
    PowerBuilderRunTime[] runtimes; 
    using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read)) 
     { 
      var reader = new XmlTextReader(fs); 
      runtimes = (PowerBuilderRunTime[])new XmlSerializer(typeof(PowerBuilderRunTime[])).Deserialize(reader); 
     } 
    return runtimes; 
} 
} 

Usted puede obtener todos los tiempos de ejecución de tipo firme, y el uso de archivos de propiedades de cada PowerBuilderRunTime a recorrer todos los nombres de archivo de cuerdas.

var runtimes = PowerBuilderRunTime.Load(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes)); 
Cuestiones relacionadas