Multiple elements with the same name, with multiple children

I ran into a problem, I need to basically deserialize this:

<?xml version="1.0" encoding="UTF-8"?>
<api_data>
  <status>ok</status>
  <sessions>
    <id>2</id>
    <sessionID>6bfd1f1a7e87a8a6ed476234ad1d6e86</sessionID>
    <gameID>1</gameID>
    <maxPlayers>8</maxPlayers>
    <hostIP>12.0.0.1</hostIP>
    <hostPort>1993</hostPort>
    <inProgress>0</inProgress>
    <timestamp>1358894690</timestamp>
  </sessions>
  <sessions>
    <id>3</id>
    <sessionID>eeb4dc2df32f885c2b7d13f28a246830</sessionID>
    <gameID>1</gameID>
    <maxPlayers>8</maxPlayers>
    <hostIP>12.0.0.1</hostIP>
    <hostPort>1993</hostPort>
    <inProgress>0</inProgress>
    <timestamp>1358894732</timestamp>
  </sessions>
</api_data>

      

And I need to convert this to usable data, its dynamic as well, so there can be no more than two session elements, maybe 4, 20 or 0, the code I have now is just broken and I was wondering what a good way to make it work?

I am currently getting to the XDocument class and the whole thing is loading. And I need to return a multidimensional array with this data.

EDIT:

Current code, completely broken:

var xmlSessions = xmlDATA.Descendants("api_data").Elements("sessions").Select(x => x);

result = new string[xmlDATA.Descendants("api_data").Count(), 7];

      

EDIT 2: More details

The way I was thinking about a multidimensional array would be as follows:

array[0,0] "ok" //Status
array[1,0 to 7] //First Session details go here
array[2,0 to 7] //Second session details go here, and so forth.

      

+3


source to share


3 answers


You can define the following class views:

public class api_data
{
    public string status { get; set; }

    [XmlElement]
    public session[] sessions { get; set; }
}

public class session
{
    public int id { get; set; }
    public string sessionID { get; set; }
    public int gameID { get; set; }
    public int maxPlayers { get; set; }
    public string hostIP { get; set; }
    public int hostPort { get; set; }
    public int inProgress { get; set; }
    public int timestamp { get; set; }
}

      

A tag key [XmlElement]

in the property sessions

that will instruct you XmlSerializer

to read / write XML using the sample schema you provided. To deserialize it, you can use XmlSerializer

as such:

//this might change, not sure how you obtain your xml, 
//but let assume you already have it available as a string
byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlData); 

var stream = new MemoryStream(xmlBytes);
XmlSerializer serializer = new XmlSerializer(typeof(api_data));
api_data apidata = (api_data)serializer.Deserialize(stream);

      

No more decorating or tweaking the XML than reading (tested and working).



EDIT: although you might want to use some other XML attributes to pass some nicer naming conventions, and also List<Session>

for loading instead of an array:

[XmlRoot("api_data")]
public class ApiData
{
    [XmlElement("status")]
    public string Status { get; set; }

    [XmlElement("sessions")]
    public List<Session> Sessions { get; set; }
}

public class Session
{
    [XmlElement("id")]
    public int ID { get; set; }

    [XmlElement("sessionID")]
    public string SessionID { get; set; }

    [XmlElement("gameID")]
    public int GameID { get; set; }

    [XmlElement("maxPlayers")]
    public int MaxPlayers { get; set; }

    [XmlElement("hostIP")]
    public string HostIP { get; set; }

    [XmlElement("hostPort")]
    public int HostPort { get; set; }

    [XmlElement("inProgress")]
    public int InProgress { get; set; }

    [XmlElement("timestamp")]
    public int TimeStamp { get; set; }
}

      

EDIT: just noticed that you need to turn this into a multidimensional array (not sure why, but you gave this legacy) At this point, you have a good object model from which you can perform this data transfer. Not sure how you are typing, but let's just assume an array like object

:

ApiData apiData = DeserializeMyApiData(); // from above
array[0][0] = apiData.Status;
for(int i = 1; i <= apiData.Sessions.Count; i++)
{
    var session = apiData.Sessions[i - 1];
    array[i] = new object[8];
    array[i][0] = session.ID;
    array[i][1] = session.SessionID;
    array[i][2] = session.GameID;
    array[i][3] = session.MaxPlayers;
    array[i][4] = session.HostIP;
    array[i][5] = session.HostPort;
    array[i][6] = session.InProgress;
    array[i][7] = session.TimeStamp;
}

      

This will go through and create your array no matter how many sessions you have.

+3


source


Can you wrap the "sessions" tags inside the session_list tag?

If you can use something like this to load it:



public class api_data {

    public class sessions {
        public string id { get; set; }
        public string sessionID { get; set; }
        // put all the other vars in here ...
    }

    public string status { get; set; }           
    public List<sessions> session_list { get; set; }

    public static api_data LoadFromXML(string xmlFile) {

        api_data localApiData;

        // serialize from file
        try {
            var xs = new XmlSerializer(typeof(api_data),
                     new XmlRootAttribute("api_data"));
            using (TextReader tr = new StreamReader(xmlFile)) {
                localApiData= xs.Deserialize(tr) as api_data;
            }
        }
        catch (Exception ex) {
            Log.LogError(string.Format(
               "Error reading api_data file {0}: {1}",
               xmlFile, ex.Message));
            return null;
        }


        return localApiData;
    }
}

      

If you cannot change the format of the xml file, you may need to load the status in your own step and then load the sessions as if the api data were a list variable, although the fact that the status is there may give you an error.

0


source


If you really want a multidimensional array, you can get this from this XML with one (somewhat long) line of code:

string[][] items = XDocument.Parse(xml).Root.Elements().Select(e => e.HasElements ? e.Elements().Select(ei => ei.Value).ToArray() : new string[]{ e.Value }).ToArray();

      

Or to make the same one statement more readable:

string[][] items = 
    XDocument.Parse(xml).Root
             .Elements().Select(e => e.HasElements ? 
                 e.Elements().Select(ei => ei.Value).ToArray() : new string[]{ e.Value })
             .ToArray();

      

From that original XML, this would create an array like this:

string[][]
{
   { "ok" },
   { "2", "6bfd1f1a7e87a8a6ed476234ad1d6e86", "1", "8", "12.0.0.1", "1993", "0", "1358894690" },
   { "3", "eeb4dc2df32f885c2b7d13f28a246830", "1", "8", "12.0.0.1", "1993", "0", "1358894732" }
}

      

If you want to get the status separately and put other values ​​in a multidimensional array, you can do this:

XDocument doc = XDocument.Parse(xml);
string status = doc.XPathSelectElement("/*/status").Value;
string[][] items = 
    doc.Root.Elements().Where(e => e.HasElements)
       .Select(e => e.Elements().Select(ei => ei.Value).ToArray()).ToArray();

      

This will do the same as above, except that the status will be a separate line, and the elements will not have the first singleton array in them:

string[][]
{
   { "2", "6bfd1f1a7e87a8a6ed476234ad1d6e86", "1", "8", "12.0.0.1", "1993", "0", "1358894690" },
   { "3", "eeb4dc2df32f885c2b7d13f28a246830", "1", "8", "12.0.0.1", "1993", "0", "1358894732" }
}

      

0


source







All Articles