2010-03-13 14 views
7

Mam trudności z wykonywaniem tego pozornie prostego zadania. Chcę ładować pliki XML z tą samą łatwością ładowania zasobów sztuki:XNA: Najlepszy sposób na załadowanie i odczytanie pliku XML?

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

Nie jestem pewien, jak to zrobić. Ta tutorial wydaje się pomocna, ale jak mogę uzyskać instancję StorageDevice?

mam coś działa teraz, ale czuje się dość 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; 
     } 

mogę zastosować to do następującego pliku XML:

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

I to jest w stanie przejść ten test jednostki:

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]); 
     } 
    } 

Zmniejszy to bieżące podejście: ładowanie pliku odbywa się słabo, a dopasowanie kluczy do wartości wydaje jak to jest znacznie więcej wysiłku niż to konieczne. Podejrzewam również, że to podejście rozpadnie się z więcej niż jednym wpisem w XML.

Nie mogę sobie wyobrazić, że jest to optymalna realizacja.

UPDATE: Idąc za radą @Peter Lillevold, zmieniłem to nieco:

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]); 
     } 
    } 

Załadunek jest wciąż dość brzydko, i jakoś nie był w stanie uzyskać jedno- linia ToDictionary do pracy z tymi dwoma lambdami. Musiałem uciekać się do pętli foreach. Co ja tam robię źle?

+0

Masz tam literówkę. @ Z mojej próbki można używać tylko z literałami łańcuchowymi, a nie ze zmiennymi łańcuchowymi lub parametrami. –

+1

Zdajesz sobie sprawę, że potok treści XNA obsługuje już pliki XML, nie? Więc dosłownie można użyć tej samej składni, której używasz do ładowania plików graficznych w celu załadowania plików XML. –

Odpowiedz

8

Istnieje również nowy i błyszczący XElement (który sport Linq to XML). Próbka ta zostanie załadowany plik xml, zajrzeć do zombie i zrzucić wartości do słownika:

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

Jeśli wolisz wybrać każdą wartość explicite (odlewanie i używać do automatycznego konwertowania do odpowiednich typów wartości) można zrobić:

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"); 

Aktualizacja: ustalające kilka literówek i czyszczenie trochę, metoda Get powinna wyglądać następująco:

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

I jaki jest sens symbolu @ przed "zombie"? –

+0

Ponadto, w jaki sposób mogę ładować plik bardziej elegancko niż uri? (coś w rodzaju potoku treści?) –

+0

Niestety, @ nie było tutaj konieczne. Używane są ciągi przedrostkowe, gdy ciąg powinien być dosłowny. Można rozważyć utworzenie niestandardowego procesora potokowego dla danego typu pliku. Spójrz na to: 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... 
} 

Czy to nie działa w XNA?

+0

Spróbuję tego. Ale jaki jest lepszy sposób na uzyskanie tego pliku xmlString z pliku? –

+0

Zaktualizowano kod, ponieważ popełniłem błąd. (doc.Load => doc.LoadXml) – TJMonk15

+0

Wygląda na to, że XmlDocument nie znajduje się w dystrybucji XNA .NET, a przynajmniej narzeka, gdy próbuję go użyć. – Arkiliknam

Powiązane problemy