Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Help needed with parsing XML and then retrieving a specific entry from a Dictionary

Discussion in 'Scripting' started by NerdRageStudios, Sep 18, 2018.

  1. NerdRageStudios

    NerdRageStudios

    Joined:
    Nov 1, 2013
    Posts:
    167
    Hi all, I am trying to expand my rather simple dialog loading system into a more robust one, but I am having an issue with the XML. I can load the xml into a dictionary just fine, I just cant work out how to retrieve a specific item without having to iterate through the entire dictionary in a non performant way (I could have thousands of entries).

    Here is my simple XML example.

    Code (CSharp):
    1. <?xml version="1.0"?>
    2. <Dialog>
    3.   <Entry>
    4.     <ID>1</ID>
    5.     <To>Jane Doe</To>
    6.     <From>John Doe</From>
    7.     <Heading>This is a heading</Heading>
    8.     <Body>This is the main body text of the document</Body>
    9.   </Entry>
    10. </Dialog>
    And here is how I load in the XML

    Code (CSharp):
    1.     public class Dialog
    2.     {
    3.         public string to;
    4.         public string from;
    5.         public string heading;
    6.         public string body;
    7.     }  
    8.  
    9. List<Dictionary<string, string>> dialog = new List<Dictionary<string, string>>();
    10.     Dictionary<string, string> obj;
    11.  
    12.     public void LoadDialog()
    13.     {
    14.         XmlDocument xmlDoc = new XmlDocument();
    15.         xmlDoc.LoadXml(dialogLoaded.text);
    16.         XmlNodeList entries = xmlDoc.GetElementsByTagName("Entry");
    17.  
    18.         foreach (XmlNode entry in entries)
    19.         {
    20.             XmlNodeList entryContent = entry.ChildNodes;
    21.             obj = new Dictionary<string, string>();
    22.  
    23.             foreach (XmlNode entryData in entry)
    24.             {
    25.                 if (entryData.Name == "ID")
    26.                     obj.Add("ID", entryData.InnerText);
    27.                 if (entryData.Name == "To")
    28.                     obj.Add("To", entryData.InnerText);
    29.                 if (entryData.Name == "From")
    30.                     obj.Add("From", entryData.InnerText);
    31.                 if (entryData.Name == "Heading")
    32.                     obj.Add("Heading", entryData.InnerText);
    33.                 if (entryData.Name == "Body")
    34.                     obj.Add("Body", entryData.InnerText);
    35.             }
    36.         }
    37.         dialog.Add(obj);
    38.     }
    What I would like to be able to do is simply pass in the ID, and return the correct dialog entry, something like this
    Code (CSharp):
    1.     public Dialog GetDialogLine(string id)
    2.     {
    3.         Dialog newDialog = new Dialog();
    4.  
    5.         // Get the data here and pass to the new class
    6.  
    7.         return newDialog;
    8.     }
    but I just cant figure out the best way to do it as I will need to know the actual entry number ie:
    Code (CSharp):
    1.  
    2. dialog[actual ID entry number-1].TryGetValue("To",out dialogTo);
    Ideally, I just want to be able to retrieve the position in the dictionary from the ID number, then fill out the values in the Class from that, but I just cant get my head around it!

    Any help would be greatly appreciated!
     
  2. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    A Dictionary stores combinations of a key and an entry. In this case your key is the ID and you entry is, well, the entry. I'd say, look at just directly deserializing the XML. That way you'd obtain a List<Entry>, which you only need to convert to a Dictionary<int, Entry> for lookup by ID.
    Code (csharp):
    1.  
    2. public class Entry
    3. {
    4.     [XmlElement(ElementName="id")]
    5.     public int Id { get; set; }
    6.     [XmlElement(ElementName="to")]
    7.     public string To { get; set; }
    8.     [XmlElement(ElementName="from")]
    9.     public string From { get; set; }
    10.     [XmlElement(ElementName="heading")]
    11.     public string Heading { get; set; }
    12.     [XmlElement(ElementName="body")]
    13.     public string Body { get; set; }
    14. }
    15.  
    16. public Dictionary<int, Entry> LookupById(List<Entry> input)
    17. {
    18.     Dictionary<int, Entry> output = new Dictionary<int, Entry>();
    19.     foreach (Entry entry in input) output.Add(entry.Id, entry);
    20.     return output;
    21. }
    22.  
     
  3. NerdRageStudios

    NerdRageStudios

    Joined:
    Nov 1, 2013
    Posts:
    167
    Hey, thanks so much for this, I am not sure I fully understand what you mean (sorry, working with XML is new to me) but I will endeavour to try and implement this suggestion :)
     
  4. NerdRageStudios

    NerdRageStudios

    Joined:
    Nov 1, 2013
    Posts:
    167
    @jvo3dc Hi, I tried to implement your suggestion but got errors. I think I did everything correctly, here is the code I am using:

    Code (CSharp):
    1. [System.Serializable]
    2.     public class Entry
    3.     {
    4.         [XmlElement(ElementName = "ID")]
    5.         public int Id { get; set; }
    6.         [XmlElement(ElementName = "To")]
    7.         public string To { get; set; }
    8.         [XmlElement(ElementName = "From")]
    9.         public string From { get; set; }
    10.         [XmlElement(ElementName = "Heading")]
    11.         public string Heading { get; set; }
    12.         [XmlElement(ElementName = "Body")]
    13.         public string Body { get; set; }
    14.     }
    15.  
    16.     [System.Serializable]
    17.     [System.Xml.Serialization.XmlRoot("Dialog")]
    18.     public class DialogEntries
    19.     {
    20.         [XmlArray("Entries")]
    21.         [XmlArrayItem("Entry", typeof(Entry))]
    22.         public Entry[] entries { get; set; }
    23.     }
    24.  
    25.     public DialogEntries dialogEntries;
    26.     public Dictionary<int, Entry> loadedDialogEntries;
    27.  
    28.     public void DeSerializeDialog()
    29.     {
    30.         dialogEntries = new DialogEntries();
    31.         XmlSerializer serializer = new XmlSerializer(typeof(DialogEntries));
    32.         StreamReader reader = new StreamReader(Application.dataPath+"/Resources/Language/" + dialogPrefix + "_" + language.ToString() + ".xml");
    33.         dialogEntries = (DialogEntries)serializer.Deserialize(reader);
    34.         reader.Close();
    35.         loadedDialogEntries = Lookup(dialogEntries.entries);
    36.     }
    37.  
    38.     public Dictionary<int, Entry> Lookup(Entry[] input)
    39.     {
    40.         Dictionary<int, Entry> output = new Dictionary<int, Entry>();
    41.         foreach (Entry entry in input) output.Add(entry.Id, entry);
    42.         return output;
    43.     }
    44.  
    45.     public Entry ReadDialog(int id)
    46.     {
    47.         return loadedDialogEntries[id];
    48.     }
    And here is the XML

    Code (CSharp):
    1. <?xml version="1.0"?>
    2. <Dialog>
    3.   <Entries>
    4.       <Entry>
    5.       <ID>1</ID>
    6.       <To>John Doe</To>
    7.       <From>Jane Doe</From>
    8.       <Heading>Heading</Heading>
    9.       <Body>This is the body</Body>
    10.     </Entry>
    11. </Entries>
    12. </Dialog>
    When run, I simply get the following error

    Code (CSharp):
    1. FormatException: Input string was not in the correct format
    2. System.Int32.Parse (System.String s, NumberStyles style, IFormatProvider provider) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Int32.cs:640)
    3. System.Xml.XmlConvert.ToInt32 (System.String s)
    4. System.Xml.Serialization.XmlCustomFormatter.FromXmlString (System.Xml.Serialization.TypeData type, System.String value)
    5. System.Xml.Serialization.XmlSerializationReaderInterpreter.GetValueFromXmlString (System.String value, System.Xml.Serialization.TypeData typeData, System.Xml.Serialization.XmlTypeMapping typeMap)
    6. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadPrimitiveValue (System.Xml.Serialization.XmlTypeMapElementInfo elem)
    7. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadObjectElement (System.Xml.Serialization.XmlTypeMapElementInfo elem)
    8. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadMembers (System.Xml.Serialization.ClassMap map, System.Object ob, Boolean isValueList, Boolean readByOrder)
    9. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadClassInstanceMembers (System.Xml.Serialization.XmlTypeMapping typeMap, System.Object ob)
    10. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadClassInstance (System.Xml.Serialization.XmlTypeMapping typeMap, Boolean isNullable, Boolean checkType)
    11. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadObject (System.Xml.Serialization.XmlTypeMapping typeMap, Boolean isNullable, Boolean checkType)
    12. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadObjectElement (System.Xml.Serialization.XmlTypeMapElementInfo elem)
    13. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadListElement (System.Xml.Serialization.XmlTypeMapping typeMap, Boolean isNullable, System.Object list, Boolean canCreateInstance)
    14. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadMembers (System.Xml.Serialization.ClassMap map, System.Object ob, Boolean isValueList, Boolean readByOrder)
    15. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadClassInstanceMembers (System.Xml.Serialization.XmlTypeMapping typeMap, System.Object ob)
    16. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadClassInstance (System.Xml.Serialization.XmlTypeMapping typeMap, Boolean isNullable, Boolean checkType)
    17. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadObject (System.Xml.Serialization.XmlTypeMapping typeMap, Boolean isNullable, Boolean checkType)
    18. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadRoot (System.Xml.Serialization.XmlTypeMapping rootMap)
    19. System.Xml.Serialization.XmlSerializationReaderInterpreter.ReadRoot ()
    20. System.Xml.Serialization.XmlSerializer.Deserialize (System.Xml.Serialization.XmlSerializationReader reader)
    21.  
    Any ideas on where I might have gone wrong implementing this? Thanks for the help!
     
  5. NerdRageStudios

    NerdRageStudios

    Joined:
    Nov 1, 2013
    Posts:
    167
    Ah, my bad, there was a blank entry that was throwing an exception... it *seems* to be working at the mo, I just need to fix some other errors before I will know for sure