Deserialising XML without a declaration or namespace in Silverlight - c#

I've been landed with a feed of XML data that I need to deserialise into objects in a Silverlight (v5) application. The data looks like:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
I've created a class at the Silverlight side, and I'm trying to get this XML - which, you'll notice, is missing a declaration and a namespace - into objects.
StringReader sr = null;
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
sr = new StringReader(data);
XmlSerializer xs = new XmlSerializer(typeof (AgentState));
AgentState agent = (AgentState) xs.Deserialize(sr);
.. but this throws an error an error in xml document (1,2), as it's missing the declaration. Even manually adding a dummy declaration gives further errors about missing namespaces.
I've found other questions about ignoring namespace/declarations in XML, but none of these seem to work in Silverlight.
Can anyone advise on the best way to get this XML deserialised into an object?

This seems to work:
public class AgentState
{
public string agentName { get; set; }
public string extension { get; set; }
public string currentlyIn { get; set; }
}
static void Main(string[] args)
{
var s = #"<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>";
XmlSerializer serializer = new XmlSerializer(typeof(AgentState));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(s));
var obj = serializer.Deserialize(ms);
}

I'm wondering what issue you have with appending the xml declaration to the string. This appears to work ok:
[System.Xml.Serialization.XmlRootAttribute("AgentState")]
public class AgentState
{
public string agentName {get; set;}
public int extension {get; set;}
public string currentlyIn {get; set;}
}
public void RunSerializer()
{
System.Xml.Serialization.XmlSerializer agent_serializer =
new System.Xml.Serialization.XmlSerializer(typeof(AgentState));
string agent_state_text = File.ReadAllText(#"C:\Temp\AgentState.xml");
Console.WriteLine(agent_state_text + Environment.NewLine);
string xml_agent_state = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + agent_state_text;
Console.WriteLine(xml_agent_state + Environment.NewLine);
AgentState agent_state = new AgentState();
using(StringReader tx_reader = new StringReader(xml_agent_state))
{
if (tx_reader != null)
{
agent_state = (AgentState)agent_serializer.Deserialize(tx_reader);
}
}
Console.WriteLine(agent_state.agentName);
Console.WriteLine(agent_state.extension);
Console.WriteLine(agent_state.currentlyIn);
}
Output:
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
<?xml version="1.0" encoding="UTF-8"?>
<AgentState>
<agentName>jbloggs</agentName>
<extension>12345</extension>
<currentlyIn>TestStatus</currentlyIn>
</AgentState>
jbloggs
12345
TestStatus

I've managed to get it working using the following code - I'm not convinced it's the "right" way to do things, but it seems to work:
string data = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);
var document = XDocument.Parse(data);
AgentState agent= (from c in document.Elements()
select new AgentState()
{
agentName = c.Element("agentName").Value,
extension = c.Element("extension").Value,
currentlyIn=c.Element("currentlyIn").Value
}).Single();
Thanks for the advice, it got me on the right track.

Related

Serializing and deserializing a collection of objects

I'm trying to create an Rssfeed reader which saves info about a podcast to a JSON file and I'm having trouble serializing and deserializing to that file.
I realize that there are other threads regarding this subject, but I cannot grasp or comprehend how to apply it to my code or the reasoning behind it.
So I have a bit of code that creates a file if it doesn't exist and writes JSON data to it which looks like:
public void SaveFile(Podcast podcast)
{
try
{
JsonSerializer serializer = new JsonSerializer();
if(!File.Exists(#"C: \Users\Kasper\Desktop\Projektuppgift\Projektuppgift - Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json"))
{
string json = JsonConvert.SerializeObject( new { Podcast = podcast });
StreamWriter sw = File.CreateText(#"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json");
using (JsonWriter writer = new JsonTextWriter(sw))
{
serializer.Serialize(writer, json);
}
}
else
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
var jsonData = File.ReadAllText(filepath);
var podcasts = JsonConvert.DeserializeObject<List<Podcast>>(jsonData) ?? new List<Podcast>();
podcasts.Add(podcast);
jsonData = JsonConvert.SerializeObject(new {PodcastList = podcasts });
File.WriteAllText(filepath, jsonData);
}
}
catch (Exception ex)
{
Console.WriteLine("IO Exception ", ex.Message);
}
}
What I can't get to work is to deserialize from this file and add an object to it. Is there an easier way to add more data to the JSON file or am I missing something?
The Podcast class looks like this:
public class Podcast
{
public string url { get; set; }
public string name { get; set; }
public int updateInterval { get; set; }
public string category { get; set; }
//public Category category = new Category();
public List<Episode> episodes { get; set; }
public Podcast(string url, string name, Category category, List<Episode> episodes, int updateInterval)
{
this.url = url;
this.name = name;
this.category = category.name;
this.episodes = episodes;
this.updateInterval = updateInterval;
}
public Podcast(Podcast p)
{
this.url = p.url;
this.name = p.name;
this.category = p.category;
this.episodes = p.episodes;
this.updateInterval = p.updateInterval;
}
}
There appear to be a couple of issues here:
You are checking for the existence of a different file than the one you are reading/writing. The former filename has extra spaces in it. The best way to avoid this problem is to use a variable to contain the filename rather than hardcoding it in three separate places.
You are inconsistent about the JSON format you are writing and reading:
When you first create the file (in the first branch), you are writing a JSON object that contains a property Podcast which then contains a single podcast.
When you attempt to read the JSON file, you are treating the entire JSON as a list of podcasts.
After tacking the new podcast onto the list, you are writing the JSON as a single object containing a PodcastList property, which then contains the list.
You need to use a consistent JSON format. I would recommend breaking your code into smaller methods to read and write the podcasts.json file like this so that it is easier to reason about:
public static List<Podcast> ReadPodcastsFromFile(string filepath)
{
if (!File.Exists(filepath)) return new List<Podcast>();
string json = File.ReadAllText(filepath);
return JsonConvert.DeserializeObject<List<Podcast>>(json);
}
public static void WritePodcastsToFile(List<Podcast> podcasts, string filepath)
{
string json = JsonConvert.SerializeObject(podcasts);
// This will overwrite the file if it exists, or create a new one if it doesn't
File.WriteAllText(filepath, json);
}
Then, you can simplify your SaveFile method down to this (I would be tempted to rename it to SavePodcast):
public void SaveFile(Podcast podcast)
{
var filepath = #"C:\Users\Kasper\Desktop\Projektuppgift\Projektuppgift-Delkurs2\Projektet\Projektet\bin\Debug\podcasts.json";
List<Podcast> podcasts = ReadPodcastsFromFile(filepath);
podcasts.Add(podcast);
WritePodcastsToFile(podcasts, filepath);
}
Notice I've also removed the exception handling from SaveFile. You should move that up to wherever SaveFile is called, so that you can take appropriate action at that point if an exception is thrown, e.g.:
try
{
SaveFile(podcast);
}
catch (Exception ex)
{
// Show a message to the user indicating that the file did not save
}
I'm just still learning c# but it might be that you deserialise into a list of podcasts and when you serialise you're serliasing into an object type.

Parsing CDATA with C# to get BaseCod64

I am trying to get the baseCode64 Data within my response data. I am getting the ReturnCode but how can I get the data inside the "Payload".
For example my response data looks like this:
<xml_response xsi:type="xsd:string"><![CDATA[<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type="application/pdf" embedded="base64">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>]]></xml_response>
To get the ReturnValue I have coded this:
XElement xmlTree = XElement.Parse(response_data);
XElement returnCode = xmlTree.Element("ReturnCode");
XText returnCode_Value = returnCode.FirstNode as XText;
String b1 = returnCode_Value.Value;
Now, how can I get the Value inside the Payload which I to convert in plaintext or create a pdf.
I tried to use the same way with paylaod but i doesn't work. I am getting nothing:
XElement returnCode = xmlTree.Element("Payload");
An if I display the Elements with:
XElement xmlTree = XElement.Parse(response_data);
XElement new_data = xmlTree.Elements();
I am just getting:
0
I has been displayed the Element Payload. This is very interesting but why?
I would simply read the content of the response using the XmlSerializer, it makes it very easy to read the data into the object, and even the decoding could be done over a hidden property
So to read the certificate request, you could go for the following 2 classes
public class CertificateRequest
{
[XmlElement("ReturnCode")]
public int ReturnCode { get; set; }
[XmlElement("Payload")]
public Payload Payload { get; set; }
}
public class Payload
{
[XmlAttribute("content_type")]
public string ContentType { get; set; }
[XmlAttribute("embedded")]
public string Embedded { get; set; }
[XmlText]
public string Value { get; set; }
[XmlIgnore]
public string DecodedValue
{
get
{
if (string.IsNullOrWhiteSpace(Value))
{
return string.Empty;
}
return System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(Value));
}
}
}
and then read the string using a memorystream to deserialize it to a certificate request object, as in the following way:
class Program
{
static CertificateRequest DeserializeRequest(string content)
{
CertificateRequest request = null;
using (MemoryStream ms = new MemoryStream())
{
byte[] data = System.Text.Encoding.UTF8.GetBytes("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + content);
ms.Write(data, 0, data.Length);
ms.Position = 0;
XmlSerializer xs = new XmlSerializer(typeof(CertificateRequest));
request = xs.Deserialize(ms) as CertificateRequest;
}
return request;
}
static void Main(string[] args)
{
string xmlAsString = #"<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type=""application/pdf"" embedded=""base64"">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>";
CertificateRequest request = DeserializeRequest(xmlAsString);
Console.WriteLine(request.Payload.Value);
Console.WriteLine(request.Payload.DecodedValue);
Console.ReadLine();
}
}
which would then print the base64 encoded value + Hello world on the second line (good one :D)
You don't show the code where you're attempting to access the value of Payload, so I'm sort of guessing what you want to do, but try this:
XElement payload = xmlTree.Element("Payload");
string b2 = payload.Value;
First off, your XML won't load because it's missing a namespace declaration for xsi. I fixed it by adding the following:
<xml_response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string" ...
Having done that, your XML contains an embedded text string literal (represented as CDATA) which is itself XML. Therefore you must:
Parse the outer XML.
Extract the character data as a string.
Parse the inner XML.
For instance:
string response_data = #"<xml_response xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xsi:type=""xsd:string""><![CDATA[<CertificateRequest><ReturnCode>0</ReturnCode><Payload content_type=""application/pdf"" embedded=""base64"">SGVsbG8gV29ybGQ=</Payload></CertificateRequest>]]></xml_response>";
var xmlTree = XElement.Parse(response_data);
var innerXml = xmlTree.Value; // Extract the string literal
var innerXmlTree = XElement.Parse(innerXml); // Parse the string literal as XML
var payload = innerXmlTree.Element("Payload").Value; // Extract the payload value
Debug.Assert(payload == "SGVsbG8gV29ybGQ="); // No assert.

Serializing XML into object - "error in xml document (2,2)

Yes, I have read other threads on this subject but I am missing something:
I am trying to deserialize an XML document that, in part, contains logged SMS messages. The XML file takes the format of:
<reports>
<report>
<sms_messages>
<sms_message>
<number>4155554432</number>
<text>Hi! How are you?</text>
</sms_message>
<sms_message>
<number>4320988876</number>
<text>Hello!</text>
</sms_message>
</sms_messages>
</report>
</reports>
My code looks like:
[XmlType("sms_message")]
public class SMSMessage
{
[XmlElement("number")]
public string Number {get;set;}
[XmlElement("text")]
public string TheText {get;set;}
}
[XmlType("report")]
public class AReport
{
[XmlArray("sms_messages")]
public List<SMSMessage> SMSMessages = new List<SMSMessage>();
}
[XmlRoot(Namespace="www.mysite.com", ElementName="reports", DataType="string", IsNullable=true)]
public class AllReports
{
[XmlArray("reports")]
public List<AReport> AllReports = new List<AReport>();
}
I am trying to serialize it like this:
XmlSerializer deserializer = new XmlSerializer(typeof(AllReports));
TextReader tr = new StreamReader(this.tbXMLPath.text);
List<AllReports> reports;
reports = (List<AllReports>)deserializer.Deserialize(tr);
tr.close();
I get the error: "There is an error in XML document (2,2)" The inner exception states,<reports xmlns=''> was not expected.
I am sure it has something to do with the serializer looking for the namespace of the root node? Is my syntax correct above with the [XmlArray("reports")] ? I feel like something is amiss because "reports" is the root node and it contains a list of "report" items, but the decoration for the root node isn't right? This is my first foray into this area. Any help is greatly appreciated.
With a minimal change to your non-compilable code
XmlSerializer deserializer = new XmlSerializer(typeof(AllReports));
TextReader tr = new StreamReader(filename);
AllReports reports = (AllReports)deserializer.Deserialize(tr);
[XmlType("sms_message")]
public class SMSMessage
{
[XmlElement("number")]
public string Number { get; set; }
[XmlElement("text")]
public string TheText { get; set; }
}
[XmlType("report")]
public class AReport
{
[XmlArray("sms_messages")]
public List<SMSMessage> SMSMessages = new List<SMSMessage>();
}
[XmlRoot("reports")]
public class AllReports
{
[XmlElement("report")]
public List<AReport> Reports = new List<AReport>();
}

Conversion from string to object in Windows 8.1 store app c#

I want to covert to string into object with value. I mean let's say i have string that has XML code inside like:
Code Snippet:
String response =#"<?xml version=""1.0"" encoding=""utf-8""?>\r\n
<Request>\r\n
<TransactionType>ADMIN</TransactionType>\r\n
<Username>abc</Username>\r\n
<Password>def</Password>\r\n
</Request>";
I have a Class that has all the properties which mentioned in Xml like
Class ResponseClass
String UserName;
String Password;
String Transaction;
How can I set all the values in ResponseClass object without string parsing?
I have tried it with serialization but it gives me some problem in windows 8.1 app store project due to limitation in API.
Is there any way to get it sorted?
Thanks
Here's a very simple way using XDocument.Parse(String) from System.Xml.Linq:
String response = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Request>
<TransactionType>ADMIN</TransactionType>
<Username>abc</Username>
<Password>def</Password>
</Request>";
var xml = XDocument.Parse(response);
var request = xml.Element("Request");
var responseObject = new ResponseClass()
{
UserName = request.Element("Username").Value,
Password = request.Element("Password").Value,
Transaction = request.Element("TransactionType").Value,
};
Or, if the Windows store apps support it, you can use the built in XmlSerializer (if not, you can just ignore this bit). Just define your class using the XmlRoot and XmlElement attributes like this:
[XmlRoot("Request")]
public class ResponseClass
{
[XmlElement("Username")]
public String UserName { get; set; }
[XmlElement("Password")]
public String Password { get; set; }
[XmlElement("TransactionType")]
public String Transaction { get; set; }
}
and then use the create an XmlSerializer and StringReader to deserialize it:
String response = #"<?xml version=""1.0"" encoding=""utf-8""?>
<Request>
<TransactionType>ADMIN</TransactionType>
<Username>abc</Username>
<Password>def</Password>
</Request>";
var serializer = new XmlSerializer(typeof(ResponseClass));
ResponseClass responseObject;
using (var reader = new StringReader(response))
{
responseObject = serializer.Deserialize(reader) as ResponseClass;
}
Unless the Xml was generated via some serializer you will have to code it manually to match. Serialization and deserialization go hand in hand. Therefore if your xml file is the product of a serializer then there would most likely be a deserializer. If you have the option of re-creating this xml file then do so using an XmlSerializer, that way you can deserialize.
If you don't want to use XmlSerializer thenThis is bit ugly but hope this will help with what you required. I have implemented something like that for some of my use. First make an extension method.
public static class TextUtil
{
public static string JustAfter(this string Str, string Seq, string SeqEnd)
{
string Orgi = Str;
try
{
int i = Str.IndexOf(Seq);
if (i < 0)
return null;
i = i + Seq.Length;
int j = Str.IndexOf(SeqEnd, i);
int end;
if (j > 0) end = j - i;
else end = Str.Length - i;
return Orgi.Substring(i, end);
}
catch (Exception)
{
return String.Empty;
}
}
}
Now you can use it as shown.
private void ParseXml(string responce) // respnce is xml string.
{
string transactionType = responce.JustAfter("<TransactionType>", "</TransactionType>");
string userName = responce.JustAfter("<Username>", "</UserName>");
string password = responce.JustAfter("<Password>", "</Password>");
ResponceClass resClass = new RespnceClass()
{
Transaction = transactionType,
UserName = userName,
Password = password
});
}

Building XML using Linq from a List<CustomClass>

I'm just trying to understand Linq and I am trying to do something that seems very simple, but I can't get it to output the way I would like. I have been stuck on this for days trying various different methods I just can't get it right.
So I have a class EarObs, it has members: eventID, icaoId, frm, sta, db.
I'm trying to build an XML document from a List. I want the XML document to look like so:
<EarObs EventId = "123456789">
<icao icaoID = "0001">
<frm frm = "01">
<sta sta = "00">
<db>87</db>
<hz>99</hz>
</sta>
<sta station = "01">
<db>79</db>
<hz>99</hz>
</sta>
</frm>
<frm frm = "02">
................
</frm>
</icao>
</EarObs>
And this would continue all the way down keeping the same order if there was more than one frame or more than one code etc.
So this is what I have been trying most recently but it still does not output they way I would like, Obs get repeated and I do not know where I am going wrong.
string eventGUID = "eventGUID";
List<EarObs> frameObsList = new List<EarObs>();
for (int frm = 2; frm > 0; frm--)
{
for (int sta = 5; sta > 0; sta--)
{
frameObsList.Add(new EarObs("KAPF", eventGUID, frm, sta, 85 + sta, 99 + sta));
cnt++;
}
}
String eventID = obsList.First().EventGUID;
List<EarObs> distinctApts =
obsList
.GroupBy(p => p.IcaoId)
.Select(g => g.First())
.ToList();
XElement xElement = new XElement("EarObs", new XAttribute("eventID", eventID),
from ea in distinctApts
orderby ea.IcaoId
select new XElement("icao", new XAttribute("code", ea.IcaoId),
from eb in obsList
where ea.IcaoId == eb.IcaoId
orderby eb.Frm
select new XElement("frm", new XAttribute("frm", eb.Frm),
from ec in obsList
where eb.Frm == ec.Frm
orderby ec.Sta
select new XElement("sta", new XAttribute("sta", ec.Sta),
new XElement("db", ec.Db),
new XElement("hz", ec.Hz)))));
Using this code I get an xml document that repeats the frame once for each station. This is not correct. I feel like this is easily done sequentially, but I'm trying to learn and this seems just so simple that I should be able to do it in Linq. I need each element in the List to only be represented in the XML document once. How do I go about this?
I would also like to expand it so that it can handle multiple eventId's as well, but that is not as important as getting the XML structure right. Any help would be much appreciated, I haven't been able to find too many example of creating an XML including the filtering of the elements using linq, most examples seem to have the List all ready structured before they create the XML.
Since you have a custom class, EarObs why not define Xml attributes to your object and serialize the object using the XmlSerlizer class? This way, you can continue use Linq on your objects, and also output your objects.
e.g. Below is a team, with players on it.
[XmlRoot("root")]
public class Team
{
private List<Player> players = new List<Player>();
[XmlElement("player")]
public List<Player> Players { get { return this.players; } set { this.players = value; } }
// serializer requires a parameterless constructor class
public Team() { }
}
public class Player
{
private List<int> verticalLeaps = new List<int>();
[XmlElement]
public string FirstName { get; set; }
[XmlElement]
public string LastName { get; set; }
[XmlElement]
public List<int> vertLeap { get { return this.verticalLeaps; } set { this.verticalLeaps = value; } }
// serializer requires a parameterless constructor class
public Player() { }
}
Once I create a team, with some players on it, I just have to do:
Team myTeamData = new Team();
// add some players on it.
XmlSerializer deserializer = new XmlSerializer(typeof(Team));
using (TextReader textReader = new StreamReader(#"C:\temp\temp.txt"))
{
myTeamData = (Team)deserializer.Deserialize(textReader);
textReader.Close();
}
The output will look like this:
<?xml version="1.0" encoding="utf-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<player>
<FirstName>dwight</FirstName>
<LastName>howard</LastName>
<vertLeap>1</vertLeap>
<vertLeap>2</vertLeap>
<vertLeap>3</vertLeap>
</player>
<player>
<FirstName>dwight</FirstName>
<LastName>howard</LastName>
<vertLeap>1</vertLeap>
</player>
</root>
The easiest way is to create a set of classes to handle the serialization like so;
public class sta
{
public int db { get; set; }
public int hz { get; set; }
[XmlAttribute()]
public string station { get; set; }
}
public class frm
{
[XmlAttribute("frm")]
public string frmID { get; set; }
[XmlElement("sta")]
public List<sta> stas { get; set; }
}
public class icao
{
[XmlAttribute]
public string icaoID { get; set; }
[XmlElement("frm")]
public List<frm> frms { get; set; }
}
public class EarObs
{
[XmlAttribute]
public string EventId { get; set; }
[XmlElement("icao")]
public List<icao> icaos { get; set; }
}
and you can use the xml serializer to serialize/deserialize. The following serializes to the structure identical to what you have;
XmlSerializer serializer = new XmlSerializer(typeof(EarObs));
EarObs obs = new EarObs() { EventId = "123456789" };
obs.icaos = new List<icao>();
obs.icaos.Add(new icao() { icaoID = "0001" });
obs.icaos[0].frms = new List<frm>();
obs.icaos[0].frms.Add(new frm() { frmID = "01" });
obs.icaos[0].frms[0].stas = new List<sta>();
obs.icaos[0].frms[0].stas.Add(new sta() { station = "00", db = 87, hz = 99 });
obs.icaos[0].frms[0].stas.Add(new sta() { station = "01", db = 79, hz = 99 });
obs.icaos[0].frms.Add(new frm() { frmID = "02" });
using (StringWriter s = new StringWriter())
{
serializer.Serialize(s, obs);
string test = s.ToString();
}
Outputs;
<?xml version="1.0" encoding="utf-16"?>
<EarObs xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" EventId="123456789">
<icao icaoID="0001">
<frm frm="01">
<sta station="00">
<db>87</db>
<hz>99</hz>
</sta>
<sta station="01">
<db>79</db>
<hz>99</hz>
</sta>
</frm>
<frm frm="02" />
</icao>
</EarObs>
Now, while this seems like a lot of trouble to go to, it's possible to use the xsd.exe tool (comes with the framework I believe), to automatically create a set of classes that match any given xml file, although it does use an intermediary xsd file (painless though). You can find out how here; How to generate .NET 4.0 classes from xsd?

Categories

Resources