Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How do I get an array from an element on XML?

Discussion in 'Scripting' started by MartinIsla, Oct 19, 2016.

  1. MartinIsla

    MartinIsla

    Joined:
    Sep 18, 2013
    Posts:
    104
    Hello :)
    We're working on a game which is basically based on dialogs. This requires a dialog tree, so I read.
    To create a dialog tree many people recommend using XML.

    TL;DR: I have an "array" of elements in an XML file. Every element on that array has another array inside. I need to get every element from that array inside the big array. How to?

    Long story:

    I never worked with XML so this is pretty hard.
    I'm following this tutorial: Saving and Loading data: XML Serializer and so far I managed to get my main text working (i.e. what the NPC says) but inside every "dialog" element there's a "responses" array with every option the player can use I can't use.
    I uploaded an example XML file here: http://pastebin.com/eLSsAX26 for better understanding.

    This is my "single dialog" class:
    Code (CSharp):
    1. //I used a singleton here since there should only be one dialog container.
    2.     private static DialogContainer _instance;
    3.     public static DialogContainer Instance
    4.     {
    5.         get
    6.         {
    7.             if (_instance == null)
    8.                 _instance = new DialogContainer();
    9.             return _instance;
    10.         }
    11.         set { _instance = value; }
    12.     }
    13.    
    14.     [XmlAttribute("id")]
    15.     public int ID;
    16.  
    17.     public string Speaker;
    18.     public string Text;
    19.  
    20.     public ResponseContainer Responses; //If I'm not wrong this should create a ResponseContainer for every single dialog, right? But this line of code breaks everything
    And this is my "Dialog Container" class:
    Code (CSharp):
    1.  
    2.     public DialogContainer()
    3.     {
    4.         Dialogs = new List<Dialog>();
    5.     }
    6.  
    7.     [XmlArray("Dialogs"), XmlArrayItem("Dialog")]
    8.     public List<Dialog> Dialogs;
    9.  
    10.     public static DialogContainer Load(string path)
    11.     {
    12.         var serializer = new XmlSerializer(typeof(DialogContainer));
    13.         using (var stream = new FileStream(path, FileMode.Open))
    14.         {
    15.             return serializer.Deserialize(stream) as DialogContainer;
    16.         }
    17.     }
    This is my SingleResponse class that's supposed to work for the options the player can choose:
    Code (CSharp):
    1. public class SingleResponse
    2. {
    3.     public string Response;
    4.  
    5.     [XmlAttribute("RedirectID")]
    6.     public int RedirectID;
    7.  
    8.     public SingleResponse() { }  
    9. }
    And this is my ResponseContainer.
    Code (CSharp):
    1. [XmlRoot("Dialog")]
    2. public class ResponseContainer
    3. {
    4.     [XmlArray("Responses"), XmlElement("Response")]
    5.     public List<SingleResponse> Responses;
    6.  
    7.     public ResponseContainer()
    8.     {
    9.         Responses = new List<SingleResponse>();
    10.     }
    11. }
    Then I created a test code that basically prints every dialog:

    Code (CSharp):
    1. public class DialogTest : MonoBehaviour {
    2.    
    3.     void Start () {
    4.         DialogContainer.Instance = DialogContainer.Load("C:\\Users\\Tincho\\Desktop\\Dialogs.xml");
    5.         foreach (Dialog d in DialogContainer.Instance.Dialogs)
    6.         {
    7.             print(d.Text);
    8.             foreach (SingleResponse r in d.Responses.Responses)
    9.             {
    10.                 print("Response N° " + r.RedirectID + ": " + r.Response);
    11.             }
    12.         }
    13.     }
    14. }
    I can properly get my "Text" elements:

    But when I add the "Response" property to my single dialog class, I get this error:


    Sorry for the long post, I tried to tell everything I know. Every little thing you can tell me to help me is appreciated :)
    Thanks!
     
  2. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    I'm not sure, but I think this:
    Code (csharp):
    1. [XmlArray("Responses"), XmlElement("Response")]
    2. public List<SingleResponse> Responses;
    Should be this:
    Code (csharp):
    1. [XmlArray("Responses"), XmlArrayItem("Response")]
    2. public List<SingleResponse> Responses;
     
  3. MartinIsla

    MartinIsla

    Joined:
    Sep 18, 2013
    Posts:
    104
    You are absolutely right! Thanks a lot! That made the error disappear, but it still doesn't work.
    I tried printing Dialog.Responses.Count for each dialog and it tells me the list is empty. Something's going wrong!
     
  4. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    That's because your current XML-structure does not offer what the XMLSerializer tries to read.

    Currently, your file provides the following structure:
    Code (CSharp):
    1. <Dialog id="2">
    2.     <Speaker>Mary</Speaker>
    3.     <Text>Nice to meet you, John!</Text>
    4.     <Responses>
    5.         <Response>My pleasure!</Response>
    6.     </Responses>
    7. </Dialog>
    while the serializer tries to read

    Code (CSharp):
    1. <Dialog id="2">
    2.     <Speaker>Mary</Speaker>
    3.     <Text>Nice to meet you, John!</Text>
    4.     <Responses>
    5.         <Responses>
    6.             <Response>
    7.                 <Response>My pleasure!</Response>
    8.             </Response>
    9.         </Responses>
    10.     </Responses>
    11. </Dialog>
    Why?
    1) The first 'Responses' element is the 'ResponseContainer Responses' field in your 'Dialog' class.
    2) The second 'Reponses' element is the 'List<SingleResponse> Responses' field in your 'ResponseContainer' class.
    3) Each 'SingleResponse' in that list is named 'Response' due to the XMLArrayItemAttribute for the list from 2)
    4) The last 'Response' element is the 'string Response' field in your 'SingleResponse' class.
     
    MartinIsla likes this.