Reputation: 436
I have come across a problem, I need to basicly 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 that to usable data, its also dynamic, so there could be more than just 2 session elements, there could be 4, 20, or 0, the code I have now is just broken, and I was wondering whats a good method to get this to work?
Currently I am up to the point of the XDocument class, with all this loaded. And I need to return a multi-dimensional 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 info
The way I was thinking the MultiDimensional Array would be is 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.
Upvotes: 4
Views: 2134
Reputation: 101652
If you really only want a multidimensional array, you can obtain this from that XML with a single (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 that same single statement a bit 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 source XML, this would produce 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 the other values in a multidimensional array, you could 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 would produce the same as above, except status would be an individual string, and items would not have the first single-element array in it:
string[][]
{
{ "2", "6bfd1f1a7e87a8a6ed476234ad1d6e86", "1", "8", "12.0.0.1", "1993", "0", "1358894690" },
{ "3", "eeb4dc2df32f885c2b7d13f28a246830", "1", "8", "12.0.0.1", "1993", "0", "1358894732" }
}
Upvotes: 0
Reputation: 23198
You can define the following class representations:
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; }
}
The key is the [XmlElement]
tag on the sessions
property, that will instruct the XmlSerializer
to read/write XML using the schema sample you provided. To deserialize it, you can use the XmlSerializer
as such:
//this might change, not sure how you obtain your xml,
//but let's 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);
Don't need any more XML adornment or setup than that to read it in (tested and working).
EDIT: Though you may want to consider using some other XML attributes to transfer to some nicer naming conventions, and we can also List<Session>
to boot 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 specify that's legacy). Well at this point, you have a nice object model from which you can do this data transfer. Not sure how you do the typing, but let's just assuming object
type array for now:
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;
}
That will go through and build up your array regardless of how many sessions you have.
Upvotes: 4
Reputation: 427
Can you wrap the 'sessions' tags inside a 'session_list' tag?
If so you could 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 might need to load the status in it's own step and then load the sessions as if the api-data was the list variable, although the fact the status is there, might give you an error.
Upvotes: 0