LINQ2XML approach to generalize iteration through xml - c#

I am trying to parse some xml files (using LINQ) to use that data to store in DB. Following is the format for one of the service, and there are dozens of them more, each with different node patterns and structure.
Is there a way to generalize this approach, so that I can iterate all these services through a single method/lines of code? without being bothered by how the childNodes are arranged ?
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<deviceState>
<id>203948-2908093-203984902-29348020-290lk</id>
<device>Mirron</device>
<variable>
<id>Plug - Meter.VI</id>
<text>
<textValue>
<value>0.000000</value>
</textValue>
</text>
</variable>
<variable>
<id>AEB</id>
<text>
<textStr>-</textStr>
</text>
</variable>
<variable>
<id>DESCRIPTION</id>
<text>
<textStr />
</text>
</variable>
<variable>
<id>VDTTM</id>
<text>
<textDate>
<date>01042016103658</date>
</textDate>
</text>
</variable>
<variable>
<id>STATUS</id>
<text>
<textValue>
<value>1.000000</value>
</textValue>
</text>
</variable>
</deviceState>
I want to achieve a functionality, where I can access values of every variable id by specifying a search filter and then access it's value directly, without being bothered by following meaningless tags.
<text><textValue> or <text><textDate> or <text><textStr/> or <text>
<textvalue><value>
something like, lets say new devices {a.id=node("id"), a.value=node("valu")}.
Currently I am using following code which does the trick but its not efficient, neither in speed, nor in manageability.
XDocument x = XDocument.Parse(_back);
string back = "";
string xvalue, yvalue;
foreach (XElement xe in x.Descendants().Take(1))
{
var s = xe.Value.IndexOf("Plug - Meter.VI");
var val = xe.Value.Substring(s, 25);
back= val.Substring(val.LastIndexOf("I")+1, 10);
break;
}
Any guidance will be highly appreciated. Thanks
Based on Monty's feedback, this is what I am using.
public protoDCM_CornerL08UMS_View()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
StartingMeter();
}
private async void StartingMeter()
{
string val="0";
max = min = 0;
await Task.Run(() =>
{
do
{
UpdateUI(val,max,min);
val = fetchData();
Double temp =0;
if (Double.TryParse(val,out temp))
{
if(min==0&&max==0)
{
min = max = temp;
}
if(temp>max)
{
max = temp;
}
if(temp<min)
{
min = temp;
}
}
val = temp.ToString();
}
while (true);
});
}
private void UpdateUI(string value, Double _max , Double _min)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
synchronizationContext.Post(new SendOrPostCallback(o =>
{
lblInstant.Text= (string)o;
}), value);
synchronizationContext.Post(new SendOrPostCallback(o =>
{
lblMax.Text = (string)o;
}), _max.ToString());
synchronizationContext.Post(new SendOrPostCallback(o =>
{
lblMin.Text = (string)o;
}), _min.ToString());
previousTime = timeNow;
}
public Stream GenerateStreamFromString(string s)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(s);
writer.Flush();
stream.Position = 0;
return stream;
}
private string fetchData()
{
String _back = HttpGet("http://xxx.xxx.xxx"+Global.Node.Current.Device.Address+"/services/devices/deviceState.xml?id=D83AE139-E0C9-4B15-B2A9-6E0B57B28ED1?type=ALL");
//_back = FormatXML(Response);
try
{
DeviceState deserializedXML = new DeviceState();
XmlSerializer serializer = new XmlSerializer(typeof(DeviceState));
using (Stream stream = GenerateStreamFromString(_back))
{
deserializedXML = (DeviceState)serializer.Deserialize(stream);
var x = (from z in deserializedXML.Variable
where z.Id == "Plug - Meter.VI"
select new
{
Id = z.Id,
value= (z.Text.TextDate == null?
(z.Text.TextStr == null
? (z.Text.TextValue == null
? "No Text Value!" : z.Text.TextValue.Value.ToString()) : z.Text.TextStr.ToString()) : z.Text.TextDate.Date.ToString()) }).FirstOrDefault();
return x.value;
}
}
catch (Exception w)
{
MessageBox.Show(w.ToString());
return "0.0";
}
}
public static string HttpGet(string URI)
{
try
{
System.Net.WebRequest req = System.Net.WebRequest.Create(URI);
req.Method = "GET";
System.Net.WebResponse resp = req.GetResponse();
System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream());
return sr.ReadToEnd().Trim();
}
catch (Exception sl)
{
return sl.ToString();
}
}

Try this....
Usings
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
Classes
[XmlRoot(ElementName = "textValue")]
public class TextValue
{
[XmlElement(ElementName = "value")]
public string Value { get; set; }
}
[XmlRoot(ElementName = "text")]
public class Text
{
[XmlElement(ElementName = "textValue")]
public TextValue TextValue { get; set; }
[XmlElement(ElementName = "textStr")]
public string TextStr { get; set; }
[XmlElement(ElementName = "textDate")]
public TextDate TextDate { get; set; }
}
[XmlRoot(ElementName = "variable")]
public class Variable
{
[XmlElement(ElementName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "text")]
public Text Text { get; set; }
}
[XmlRoot(ElementName = "textDate")]
public class TextDate
{
[XmlElement(ElementName = "date")]
public string Date { get; set; }
}
[XmlRoot(ElementName = "deviceState")]
public class DeviceState
{
[XmlElement(ElementName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "device")]
public string Device { get; set; }
[XmlElement(ElementName = "variable")]
public List<Variable> Variable { get; set; }
}
code
try
{
DeviceState deserializedXML = new DeviceState();
// Deserialize to object
XmlSerializer serializer = new XmlSerializer(typeof(DeviceState));
using (FileStream stream = File.OpenRead(#"xml.xml"))
{
deserializedXML = (DeviceState)serializer.Deserialize(stream);
// Now get all your IDs
List<string> IDs = (from xml in deserializedXML.Variable select xml.Id).ToList();
} // Put a break-point here, then mouse-over IDs and you will see all your IDs... deserializedXML contains the entire object if you want anythin else ....
}
catch (Exception)
{
throw;
}
I read your XML from a file (xml.xml) that is in the application *.exe folder, you will need to adapt this solution depending on your specific requirements....
Is this what you need?....
try
{
XmlSerializer DeserializerPlaces = new XmlSerializer(typeof(DeviceState));
string html = string.Empty;
string url = #"https://<Your URL>";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
//request.AutomaticDecompression = DecompressionMethods.GZip;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
DeviceState dezerializedXML = (DeviceState)DeserializerPlaces.Deserialize(reader);
//html = reader.ReadToEnd();
}
//Console.WriteLine(html);
}
catch (System.Exception)
{
throw;
}
You would call that every 5 seconds and update your UI (from dezerializedXML properties)

Related

First serialization success, second fails? Why?

I have a method that serialize communication data. First the parameters is serialized into the main CallInformation object as string, then the CallInformation object will also be serialized and then later written to file :
_jSONSettings = new System.Runtime.Serialization.Json.DataContractJsonSerializerSettings();
_jSONSettings.DateTimeFormat = new DateTimeFormat("yyyy-MM-ddThh:mm:ss.fffZ");
_xmlWriterSettings = new System.Xml.XmlWriterSettings() { Indent = true };
var callInformation = logEvent.Properties.Values.First() as CallInformation;
DataContractJsonSerializer serializer;
if (TakeCharsInParameterObject > 0)
{
var counter = 0;
foreach (object param in callInformation.Parameters)
{
using (var stream = new MemoryStream())
{
serializer = new DataContractJsonSerializer(param.GetType(), _jSONSettings);
using (var xmlWriter = System.Xml.XmlWriter.Create(stream, _xmlWriterSettings))
{
serializer.WriteObject(xmlWriter, param);
stream.Flush();
stream.Position = SkipCharsInParameterObject;
using (var streamReader = new StreamReader(stream))
{
var buffer = new char[TakeCharsInParameterObject];
if (streamReader.Peek() >= 0)
{
streamReader.Read(buffer, 0, buffer.Length);
counter++;
callInformation.SerializedParameters += "{Parameter" + counter + ": " + new string(buffer) + "}";
}
}
}
}
}
}
var restult = "";
using (var stream = new MemoryStream())
{
serializer = new DataContractJsonSerializer(typeof(CallInformation), _jSONSettings);
using (var xmlWriter = System.Xml.XmlWriter.Create(stream, _xmlWriterSettings))
{
serializer.WriteObject(xmlWriter, callInformation);
stream.Flush();
stream.Position = 0;
using (var streamReader = new StreamReader(stream))
{
if (streamReader.Peek() >= 0)
restult = streamReader.ReadToEnd();
}
}
}
return restult;
The serialization for parameters works great but the second part do not. After stream.WriteObject the lengh of the stream and the position is still 0?
The CallInformation is a simple DataContract class that looks like this :
[DataContract]
public class CallInformation
{
public CallInformation()
{ }
[DataMember]
public string Address { get; set; }
[DataMember]
public Boolean IsEmpty { get; set; }
[DataMember]
public Boolean IsFaulted { get; set; } = false;
[DataMember]
public string Action { get; set; }
[DataMember]
public CallOrder CallDirection { get; set; }
public DateTime EventTime { get; set; } = DateTime.Now;
[DataMember]
public TimeSpan Duration { get; set; }
[DataMember]
public Boolean IsCallback { get; set; } = false;
[DataMember]
public string LogSource { get; set; } = "Unknown";
[DataMember]
public string SerializedParameters { get; set; } = "";
public List<object> Parameters { get; set; } = new List<object>();
[DataMember]
public string EventTimeDisplay
{
get { return EventTime.ToString("HH:mm:ss.fffffff"); }
set { }
}
}
Why is not the second serialization working?
XmlWriter has got internal buffer, you need to flush it to have stream position changed:
serializer.WriteObject(xmlWriter, callInformation);
xmlWriter.Flush();
stream.Position = 0;

How to Desalinize XML API Response with Multiple Attributes with same name

I Want to read this an XML Respones of a web api
I Want to Deserialize it but i am getting an error
i have already read many documentary on this topic but i cant resolve this on
<ArrayOfServiceAreas xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://tempuri.org/">
<ServiceAreas>
<City>
<string>ABDUL HAKIM /TULAMBA</string>
<string>ABOTTABAD</string>
<string>AHMED PUR EAST</string>
<string>ALI PUR</string>
<string>ALI PUR CHATTA</string>
<string>ARIF WALA</string>
<string>ATTOCK</string>
<string>BADIN</string>
<string>BAGH (AJK)</string>
<string>BANU</string>
<string>BAT KHELA</string>
<string>BAWALNAGAR</string>
<string>BHAI PHERU</string>
<string>BHAKKAR</string>
<string>BHALWAL</string>
<string>BHAWALPUR</string>
<string>BUREWALA</string>
<string>CHAKWAL</string>
<string>CHAMAN</string>
<string>CHARSADA</string>
<string>CHICHAWATNI</string>
<string>CHINNIOT</string>
<string>CHISTIAN</string>
<string>CHITRAL</string>
<string>D.G. KHAN</string>
<string>D.I. KHAN</string>
<string>DADU</string>
<string>DADYAL (AJK)</string>
<string>DALBANDIN</string>
<string>DARA ADAM KHEL</string>
<string>DARGAI</string>
</City>
</ServiceAreas>
</ArrayOfServiceAreas>
In C# i have created two classes given below to deserialize object
[Serializable]
public class City
{
[System.Xml.Serialization.XmlElement("string")]
public string[] String { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("ArrayOfServiceAreas")]
public class ArrayOfServiceAreas
{
[XmlArray("ServiceAreas")]
[XmlArrayItem("City", typeof(City))]
public City[] City { get; set; }
}
This is the controller where i am calling the above classes
with XML Serializer
public ActionResult City()
{
string Perameters = $"username={"myusername"}&password={"mypassword"}&AccountNo={"somenumber"}";
string u = "http://mraabta.mulphico.pk/mnpconsignments/pushtomnp.asmx/Get_Cities?"+Perameters;
var client = new RestClient(u);
var request = new RestRequest(Method.GET);
request.RequestFormat = DataFormat.Xml;
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
var responce = client.Execute(request);
//var r = JsonConvert.DeserializeObject<dynamic>(responce.Content);
System.IO.StringReader SR = new System.IO.StringReader(responce.Content.ToString());
XmlSerializer serializer = new XmlSerializer(typeof(MNP_Plus.Dserializer.MNPCities.ArrayOfServiceAreas));
MNP_Plus.Dserializer.MNPCities.ArrayOfServiceAreas List = (MNP_Plus.Dserializer.MNPCities.ArrayOfServiceAreas)serializer.Deserialize(SR);
return View();
}
Response Content Is Given Above in XML Form which i want to read.
it gives an error
There is an error in XML document (2, 2).
How can i resolve this.
Didn't found any solution for my Question in time so I did it the old fashioned way
If someone else get into this mess then the solution could be
private List<string> GetCities(string Responce)
{
List<string> list = new List<string>();
bool Collection = false;
string item = "";
int count = 0;
foreach (char i in Responce)
{
if (Collection)
{
if(i == '<') { list.Add(item); item = ""; Collection = false; }
else { item = item + i; }
}
if (count == 0) { if (i == '<') { count = 1; } }
else if (count == 1) { if (i == 's') { count = 2; } else { count = 0; } }
else if (count == 2) { if (i == 't') { count = 3; } else { count = 0; } }
else if (count == 3) { if (i == 'r') { count = 4; } else { count = 0; } }
else if (count == 4) { if (i == 'i') { count = 5; } else { count = 0; } }
else if (count == 5) { if (i == 'n') { count = 6; } else { count = 0; } }
else if (count == 6) { if (i == 'g') { count = 7; } else { count = 0; } }
else if (count == 7) { if (i == '>') { Collection = true; } count = 0; }
}
return list;
}
Make changes according to your problem.
The issue is in your mapping classes.
To make your life easier , you can use an online xml2csharp online tool to get the proper POCOs. Here
They should look like this :
[XmlRoot(ElementName = "City", Namespace = "http://tempuri.org/")]
public class City
{
[XmlElement(ElementName = "string", Namespace = "http://tempuri.org/")]
public List<string> String { get; set; }
}
[XmlRoot(ElementName = "ServiceAreas", Namespace = "http://tempuri.org/")]
public class ServiceAreas
{
[XmlElement(ElementName = "City", Namespace = "http://tempuri.org/")]
public City City { get; set; }
}
[XmlRoot(ElementName = "ArrayOfServiceAreas", Namespace = "http://tempuri.org/")]
public class ArrayOfServiceAreas
{
[XmlElement(ElementName = "ServiceAreas", Namespace = "http://tempuri.org/")]
public ServiceAreas ServiceAreas { get; set; }
[XmlAttribute(AttributeName = "xsd", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsd { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
[XmlAttribute(AttributeName = "xmlns")]
public string Xmlns { get; set; }
}
I was able to read your XML file without any issues.
Here is the serializer I used :
public class Serializer
{
public T Deserialize<T>(string input) where T : class
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
using (StringReader stringReader = new StringReader(input))
{
return (T)xmlSerializer.Deserialize(stringReader);
}
}
public string Serialize<T>(T ObjectToSerialize)
{
XmlSerializer xmlSerializer = new XmlSerializer(ObjectToSerialize.GetType());
StringBuilder builder = new StringBuilder();
using (StringWriterWithEncoding textWriter = new StringWriterWithEncoding(builder, Encoding.UTF8))
{
xmlSerializer.Serialize(textWriter, ObjectToSerialize);
return textWriter.ToString();
}
}
}
public class StringWriterWithEncoding : StringWriter
{
Encoding encoding;
public StringWriterWithEncoding(StringBuilder builder, Encoding encoding)
: base(builder)
{
this.encoding = encoding;
}
public override Encoding Encoding
{
get { return encoding; }
}
}
And Finally here is the execution:
var serializer = new Serializer();
//I used a local file for testing, but it should be the same thing with your api response
var xmlInputData = File.ReadAllText(#"MyXmlPath");
var output = serializer.Deserialize<ArrayOfServiceAreas>(xmlInputData);

Deserialize httpWebResponse into object

I received the web response and need to desterilize into the list. I get an error "Root element is missing". Would someone tell me how to solve it. Thanks.
I debug the code and get the response text:
<ArrayOfLocation xmlns="http://schemas.datacontract.org/2004/07/Ordinging.Objects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Location>
<locationID>401</locationID>
<locationName>Burnaby</locationName>
</Location>
<Location>
<locationID>101</locationID>
<locationName>Vancouver</locationName>
</Location>
</ArrayOfLocation>
My code to desterilize:
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
result = reader.ReadToEnd();
XmlSerializer serializer = new XmlSerializer(typeof(List<LocationList.Location>));
List<LocationList.Location> data = new List<LocationList.Location>();
data = serializer.Deserialize(reader) as List<LocationList.Location>;
}
The Location Class in my app:
public class LocationList
{
private List<Location> locations = null;
[XmlElement("loctions")]
public List<Location> locs
{
get { return locations; }
set { locations = value; }
}
public class Location
{
public string locationName { get; set; }
public Int64 locationID { get; set; }
public Location(string name, Int64 id)
{
locationID = id;
locationName = name;
}
public Location() { }
}
}
One way to do this. Change it to use the xml from your response instead. I used a hardcoded string just for my testing).
Edit : Added helper function to ignore namespace if you need to do that. Otherwise xml should match namespace.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace TestCodeApp {
class TestCode {
static void Main () {
string xmlString = #"
<ArrayOfLocation xmlns='http://schemas.datacontract.org/2004/07/Ordinging.Objects' xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>
<Location>
<locationID>401</locationID>
<locationName>Burnaby</locationName>
</Location>
<Location>
<locationID>101</locationID>
<locationName>Vancouver</locationName>
</Location>
</ArrayOfLocation>";
StringReader stringReader = new StringReader (xmlString);
XmlSerializer serializer = new XmlSerializer (typeof (List<Location>), new XmlRootAttribute ("ArrayOfLocation"));
List<Location> locations = (List<Location>) serializer.Deserialize (new XmlTextReaderHelper(stringReader));
foreach (Location item in locations) Console.WriteLine (item);
}
}
public class XmlTextReaderHelper : XmlTextReader {
public XmlTextReaderHelper (System.IO.TextReader reader) : base (reader) { }
public override string NamespaceURI {
get { return ""; }
}
}
public class Location {
public int locationID { get; set; }
public string locationName { get; set; }
public override string ToString () {
return "ID: " + locationID + " - " + locationName;
}
}
}

get int value from XmlTextAttribute when deserialize xml to class in c#

xml as below :
<Item>
<Winner>2</Winner>
</Item>
For my class definition I have the following:
public enum HomeOrAwayTeamType {Home =1,Away =2,Draw =3,NA = 0};
class Item
{
[XmlIgnore]
public virtual HomeOrAwayTeamType Winner { get; set; }
[XmlElement(ElementName = "Winner"), XmlText]
public virtual string WinnerSerializer
{
get { return this.Winner.ToString(); }
set
{
//get 'Away' from HomeOrAwayTeamType
this.Winner = (HomeOrAwayTeamType)2; //ok
this.Winner = (HomeOrAwayTeamType)Convert.ToInt32("2"); //ok
this.Winner = (HomeOrAwayTeamType)int.parse("2"); //ok
//get 'NA' from HomeOrAwayTeamType
this.Winner = (HomeOrAwayTeamType)Convert.ToInt32(value); //fail
this.Winner = (HomeOrAwayTeamType)int.parse(value); //fail
}
}
}
string xml = ""; //xml code
Item model = default(Item);
using (var reader = XmlReader.Create(new StringReader(xml)))
{
var serializer = new XmlSerializer(typeof(Item));
model = (Item)serializer.Deserialize(reader);
}
hi, guys..
How get int value from XmlTextAttribute?
Please help~
Your xml string is empty. This works for me:
string xml = "<Item><Winner>2</Winner></Item>"; //xml code
this.Winner gets set to Away

Parse XML file and populate object with values

After I send a request with the required parameters in the response I get the following XML:
<content>
<main>
<IMGURL>image url</IMGURL>
<IMGTEXT>Click Here</IMGTEXT>
<TITLE>image title</TITLE>
<IMGLINK>image link</IMGLINK>
</main>
</content>
and I also made the following two classes:
[Serializable]
public class content
{
private Main _main;
public content()
{
_main = new Main();
}
public Main Main
{
get { return _main; }
set { _main = value; }
}
}
[Serializable]
public class Main
{
public string IMGURL { get; set; }
public string IMGTEXT { get; set; }
public string TITLE { get; set; }
public string IMGLINK { get; set; }
}
While debugging I can see that in the response I get the wanted results. However I'm having troubles deserializing the XML and populating the object.
Call to the method:
public static class ImageDetails
{
private static string _url = ConfigurationManager.AppSettings["GetImageUrl"];
public static content GetImageDetails(string ua)
{
var contenta = new content();
_url += "&ua=" + ua;
try
{
WebRequest req = WebRequest.Create(_url);
var resp = req.GetResponse();
var stream = resp.GetResponseStream();
//var streamreader = new StreamReader(stream);
//var content = streamreader.ReadToEnd();
var xs = new XmlSerializer(typeof(content));
if (stream != null)
{
contenta = (content)xs.Deserialize(stream);
return contenta;
}
}
catch (Exception ex)
{
}
return new content();
}
}
The serializer is case-sensitive. You either need to rename the property content.Main to main or add the attribute [XmlElement("main")] to it.

Categories