2010-03-13 13 views
7

Tengo dificultades para realizar esta tarea aparentemente simple. Quiero cargar archivos XML con la misma facilidad para cargar elementos artísticos:XNA: ¿La mejor manera de cargar y leer un archivo XML?

 content = new ContentManager(Services); 
     content.RootDirectory = "Content"; 
     Texture2d background = content.Load<Texture2D>("images\\ice"); 

No sé cómo hacerlo. Este tutorial parece útil, pero ¿cómo obtengo una instancia de StorageDevice?

tengo algo de trabajo, pero se siente muy hacky:

public IDictionary<string, string> Get(string typeName) 
     { 
      IDictionary<String, String> result = new Dictionary<String, String>(); 
      xmlReader.Read(); // get past the XML declaration 

      string element = null; 
      string text = null; 

      while (xmlReader.Read()) 
      { 

       switch (xmlReader.NodeType) 
       { 
        case XmlNodeType.Element: 
         element = xmlReader.Name; 
         break; 
        case XmlNodeType.Text: 
         text = xmlReader.Value; 
         break; 
       } 

       if (text != null && element != null) 
       { 
        result[element] = text; 
        text = null; 
        element = null; 
       } 

      } 
      return result; 
     } 

aplico esto a la siguiente archivo XML:

<?xml version="1.0" encoding="utf-8" ?> 
<zombies> 
    <zombie> 
    <health>100</health> 
    <positionX>23</positionX> 
    <positionY>12</positionY> 
    <speed>2</speed> 
    </zombie> 
</zombies> 

y es capaz de pasar esta prueba de unidad:

internal virtual IPersistentState CreateIPersistentState(string fullpath) 
    { 
     IPersistentState target = new ReadWriteXML(File.Open(fullpath, FileMode.Open)); 
     return target; 
    } 

    /// <summary> 
    ///A test for Get with one zombie. 
    ///</summary> 
    //[TestMethod()] 
    public void SimpleGetTest() 
    { 
     string fullPath = "C:\\pathTo\\Data\\SavedZombies.xml"; 
     IPersistentState target = CreateIPersistentState(fullPath); 
     string typeName = "zombie"; 

     IDictionary<string, string> expected = new Dictionary<string, string>(); 
     expected["health"] = "100"; 
     expected["positionX"] = "23"; 
     expected["positionY"] = "12"; 
     expected["speed"] = "2"; 

     IDictionary<string, string> actual = target.Get(typeName); 

     foreach (KeyValuePair<string, string> entry in expected) 
     { 
      Assert.AreEqual(entry.Value, expected[entry.Key]); 
     } 
    } 

Desventajas del enfoque actual: la carga de archivos se realiza mal y las claves de los valores coinciden como si fuera mucho más esfuerzo de lo necesario. Además, sospecho que este enfoque se derrumbaría con más de una entrada en el XML.

No me puedo imaginar que esta sea la implementación óptima.

ACTUALIZACIÓN: Siguiendo el consejo de @Peter Lillevold, he cambiado esto un poco:

public IDictionary<string, string> Get(string typeName) 
    { 
     IDictionary<String, String> result = new Dictionary<String, String>(); 

     IEnumerable<XElement> zombieValues = root.Element(@typeName).Elements(); 

     //result["health"] = zombie.Element("health").ToString(); 

     IDictionary<string, XElement> nameToElement = zombieValues.ToDictionary(element => element.Name.ToString()); 

     foreach (KeyValuePair<string, XElement> entry in nameToElement) 
     { 
      result[entry.Key] = entry.Value.FirstNode.ToString(); 
     } 

     return result; 
    } 

    public ReadWriteXML(string uri) 
    { 
     root = XElement.Load(uri); 
    } 

    internal virtual IPersistentState CreateIPersistentState(string fullpath) 
    { 
     return new ReadWriteXML(fullpath); 
    } 

    /// <summary> 
    ///A test for Get with one zombie. 
    ///</summary> 
    [TestMethod()] 
    public void SimpleGetTest() 
    { 
     IPersistentState target = CreateIPersistentState("../../../path/Data/SavedZombies.xml"); 
     string typeName = "zombie"; 

     IDictionary<string, string> expected = new Dictionary<string, string>(); 
     expected["health"] = "100"; 
     expected["positionX"] = "23"; 
     expected["positionY"] = "12"; 
     expected["speed"] = "2"; 

     IDictionary<string, string> actual = target.Get(typeName); 

     foreach (KeyValuePair<string, string> entry in expected) 
     { 
      Assert.AreEqual(entry.Value, actual[entry.Key]); 
     } 
    } 

La carga sigue siendo bastante malo, y de alguna manera yo no era capaz de conseguir el uno línea ToDictionary para trabajar con esas dos lambdas. Tuve que recurrir a ese ciclo foreach. ¿Qué estoy haciendo mal allí?

+0

Tiene un error tipográfico allí. El @ de mi muestra solo se puede usar con literales de cadena, no con variables de cadena o parámetros. –

+1

Te das cuenta de que el canal de contenido XNA YA admite archivos XML, ¿no? Entonces, literalmente puede usar la misma sintaxis que usa para cargar archivos de arte para cargar archivos XML. –

Respuesta

8

También está el nuevo y brillante XElement (que se divierte Linq to XML). Esta muestra se carga un archivo XML, buscar el zombi y volcar los valores en un diccionario:

var doc = XElement.Load("filename"); 
var zombieValues = doc.Element("zombie").Elements(); 
var zombieDictionary = 
    zombieValues.ToDictionary(
     element => element.Name.ToString(), 
     element => element.Value); 

Si prefiere seleccionar cada valor de forma explícita (y utiliza el casting para convertir automáticamente en los tipos de valores propios) que pueda hacer:

var zombie = doc.Element("zombie"); 
var health = (int)zombie.Element("health"); 
var positionX = (int)zombie.Element("positionX"); 
var positionY = (int)zombie.Element("positionY"); 
var speed = (int)zombie.Element("speed"); 

actualización: la fijación de algunos errores tipográficos y limpiar un poco, su método de Get debería tener este aspecto:

public IDictionary<string, string> Get(string typeName) 
{ 
    var zombie = root.Element(typeName); 
    return zombie.Elements() 
      .ToDictionary(
        element => element.Name.ToString(), 
        element => element.Value); 
} 
+0

Y, ¿cuál es el punto del símbolo @ frente a "zombie"? –

+0

Además, ¿cómo puedo cargar el archivo de forma más elegante que un uri? (algo así como una tubería de contenido?) –

+0

Lo sentimos, @ no era necesario aquí. Se usa cadenas de prefijos cuando la cadena debe ser literal. Podría considerar hacer un procesador de canalización personalizado para su tipo de archivo. Eche un vistazo a esto: http://msdn.microsoft.com/en-us/library/bb447754.aspx –

2
System.Xml.XmlDocument doc = new System.Xml.XmlDocument(); 
doc.LoadXml(xmlString); 

string health = doc["zombies"]["zombie"]["health"].InnerText; 
// etc.. 

// or looping 

foreach(XmlNode node in doc["zombies"].ChildNodes) 
{ 
    string health = node["health"].InnerText; 
    // etc... 
} 

¿O no funciona en XNA?

+0

Voy a intentar eso. Pero, en primer lugar, ¿cuál es la mejor manera de obtener ese xmlString de un archivo? –

+0

Actualicé el código porque cometí un error. (doc.Load => doc.LoadXml) – TJMonk15

+0

Parece que XmlDocument no está incluido en la distribución XNA de .NET, o al menos se queja cuando trato de usarlo. – Arkiliknam

Cuestiones relacionadas