access sub XML values in sms web service that hasnt value in standard way - c#

I have following xml :
in other standard xml like this question here we have no problem but in php web service :
http://sandoghche.com/WebService
for inbox check my inbox xml is this:
<?xml version=\"1.0\" encoding=\"UTF-8\" ?>
<Result>
<text_message>
<id>509</id>
<message><![CDATA[a]]></message>
<time>1323519941</time>
<message_phone>
<cellphone>09196070718</cellphone>
</message_phone>
</text_message>
<text_message>
<id>507</id>
<message>1</message>
<time>1323519803</time>
<message_phone>
<cellphone>09360437392</cellphone>
</message_phone>
</text_message>
<text_message>
<id>303</id>
<message><![CDATA[smsm text]]></message>
<time>1318343296</time>
<message_phone>
<cellphone>09354338365</cellphone>
</message_phone>
</text_message>
<text_message>
<id>219</id>
<message><![CDATA[my sms texts here]]></message>
<time>1316154042</time>
<message_phone>
<cellphone>09127930265</cellphone>
</message_phone>
</text_message>
<text_message>
<id>217</id>
<message>2</message>
<time>1316090189</time>
<message_phone>
<cellphone>09195533234</cellphone>
</message_phone>
</text_message>
</Result>
for this I wrote the following code
public class SMSData
{
public int id { set; get; }
public string message { set; get; }
public string cellphone { set; get; }
}
string data = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><Result><text_message><id>509</id><message><![CDATA[a]]></message><time>1323519941</time><message_phone><cellphone>09196070718</cellphone></message_phone></text_message><text_message><id>507</id><message>1</message><time>1323519803</time><message_phone><cellphone>09360437392</cellphone></message_phone></text_message><text_message><id>303</id><message><![CDATA[ممنون.شما؟]]></message><time>1318343296</time><message_phone><cellphone>09354378365</cellphone></message_phone></text_message><text_message><id>219</id><message><![CDATA[سلام اقاي طباطبايي لينك مربوط به ورود اعضا به وبلاگ روي صفحه فيلتر مي رود با احترام احدي]]></message><time>1316154042</time><message_phone><cellphone>09127960265</cellphone></message_phone></text_message><text_message><id>217</id><message>2</message><time>1316090189</time><message_phone><cellphone>09195523234</cellphone></message_phone></text_message></Result>";
Stream s = GenerateStreamFromString(data);
XmlReader xr = XmlReader.Create(s);
while (xr.Read())
{
if ((xr.NodeType == XmlNodeType.Element) && (xr.Name == "text_message"))
{
if (xr.HasValue)
{
string id = xr.GetAttribute("id");
string message = xr.GetAttribute("message");
}
}
}
and this code
Dictionary<int, string> myDictionary = new Dictionary<int, string>();
XmlDocument xml = new XmlDocument();
xml.LoadXml(data);
XmlNodeList xnList = xml.SelectNodes("/Result");
foreach (XmlNode xn in xnList)
{
but can't catch values on this xml document.
Is there a clean way to do this?

It is easier to use the built-in Serializers for these kind of things, like the XmlSerializer or the DataContractSerializer. I used the latter and tested your XML with the following snippet.
First annotate your SMSData class and create a new class to hold the Message_phone elements in a new class:
[DataContract(Name="text_message",Namespace = "")]
public class SMSData
{
[DataMember(Order =1)]
public int id { set; get; }
[DataMember(Order = 2)]
public string message { set; get; }
[DataMember(Order = 3)]
public int time { set; get; }
[DataMember(Order = 4)]
public Message_phone message_phone { set; get; }
// for easy access to cellphone
public string cellphone {
get
{
return message_phone!=null?message_phone.cellphone:null;
}
}
}
[DataContract(Name="message_phone",Namespace = "")]
public class Message_phone
{
[DataMember]
public string cellphone { set; get; }
}
and then create an instance of the DataContractSerializer for your array of SMSData, and call ReadObject on it like so:
Stream s = GenerateStreamFromString(data);
var ds = new DataContractSerializer(
typeof(SMSData[]), // array of SMSdata
"Result", // top level element
"", // empty namespace
new List<Type> { typeof(SMSData[])} );
var smsdata = (SMSData[]) ds.ReadObject(XmlReader.Create(s));
// smsdata now holds the SMSData from the Xml file

Related

How to deserialize this xml to objects?

I'm fetching data in the format of this:
<ReplyUserAccount xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" requestid="" version="1.0" xmlns="url">
<Sender partnerid="xx">xx</Sender>
<Users>
<User method="GET" ResultCode="OK" Description="">
<Guid>xx</Guid>
<FirstName>xx</FirstName>
<LastName>xx</LastName>
<Phone>xx</Phone>
<Mobile>xx</Mobile>
<Email>xx</Email>
<EmplNo>xx</EmplNo>
<TacPermission />
<InvPermission>xx</InvPermission>
<CustomerId>xx</CustomerId>
</User>
</Users>
</ReplyUserAccount>
With the following C# objects:
[XmlRoot("ReplyUserAccount")]
public class ReplyUserAccount
{
[XmlElement("Users")]
public Users Users{ get; set; }
}
[XmlType("Users")]
public class Users
{
[XmlElement("User")]
public List<User> UserList{ get; set; }
}
[XmlType("User")]
public class User
{
[XmlElement("EmplNo")]
public string Id{ get; set; }
[XmlElement("Guid")]
public string Guid { get; set; } = null;
[XmlElement("Email")]
public string Email { get; set; }
[XmlElement("FirstName")]
public string FirstName { get; set; }
[XmlElement("LastName")]
public string LastName { get; set; }
public bool Active { get; set; } = true;
public string PhoneNumber { get; set; } = null;
}
And the following deserializing:
var result = await httpClient.GetAsync(url);
var xdoc = XDocument.Parse(await result.Content.ReadAsStringAsync());
XmlSerializer serializer = new XmlSerializer(typeof(ReplyUserAccount));
var content = xdoc.ToString();
TextReader reader = new StringReader(content);
var res = (ReplyUserAccount)serializer.Deserialize(reader);
But I get the following error:
InvalidOperationException: <ReplyUserAccount xmlns='xxx'> was not expected.
I'm a little bit lost as to how to properly deserialize this specific xml data. Any and all help with regards to this is greatly appreciated.
To fix the error, You have to remove xmlns and xsi in xml text before deserialize. You can remove xmlns and xsi like this:
var content = xdoc.ToString();
string strXMLPattern = #"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";
content = Regex.Replace(content, strXMLPattern, "");
Therefore the method should be as follows
var result = await httpClient.GetAsync(url);
var xdoc = XDocument.Parse(await result.Content.ReadAsStringAsync());
XmlSerializer serializer = new XmlSerializer(typeof(ReplyUserAccount));
var content = xdoc.ToString();
string strXMLPattern = #"xmlns(:\w+)?=""([^""]+)""|xsi(:\w+)?=""([^""]+)""";
content = Regex.Replace(content, strXMLPattern, "");
TextReader reader = new StringReader(content);
var res = (ReplyUserAccount)serializer.Deserialize(reader);

Deserialising XML with different element name in c#

XML:
<?xml version="1.0" encoding="UTF-8"?>
<Images>
<I0>
<Path>123.com</Path>
<I0>
<I1>
<Path>123.com</Path>
<I1>
<I2>
<Path>123.com</Path>
<I2>
</Images>
Can serializer.Deserialize() be used to get tags with different names into a collection?
currently, in my object I have:
C#:
public class rootObject
{
[XmlElement(ElementName = "I0")]
public I0 I0 { get; set; }
[XmlElement(ElementName = "I1")]
public I1 I1 { get; set; }
[XmlElement(ElementName = "I2")]
public I2 I2 { get; set; }
}
But I would like to have (Because Images can have more or fewer elements):
public class rootObject
{
public List<I> Is { get; set; }
}
You can do what you are suggesting you just merely need to pass in the type argument in your class doing the generic. The key point to remember when you do a deserialization routine is that the routine needs to know the sub reference. So if I was to say string.Deserialize it would bomb. It would need to know a reference string.Deserialize> where Sub could be the class object that may change.
Say I have a base class and I want 'T' to be a type I can change for extensible abilities later.
[Serializable]
public class Test<T> where T : class
{
public Test() { }
public int TestId { get; set; }
public string Name { get; set; }
public List<T> Shipments { get; set; }
}
I want to test this with two classes I just make up that have different properties slightly
[Serializable]
public class Sub1
{
public int Id { get; set; }
public string Desc { get; set; }
}
[Serializable]
public class Sub2
{
public int IdWhatever { get; set; }
public string DescWhatever { get; set; }
}
Now let's do a main program and test serialization.
class Program
{
static void Main(string[] args)
{
var serializeTest = new Test<Sub1> { TestId = 1, Name = "Test", Shipments = new List<Sub1> { new Sub1 { Id = 1, Desc = "Test" }, new Sub1 { Id = 2, Desc = "Test2" } } };
var serializeTest2 = new Test<Sub2> { TestId = 1, Name = "Test", Shipments = new List<Sub2> { new Sub2 { IdWhatever = 1, DescWhatever = "Test" }, new Sub2 { IdWhatever = 2, DescWhatever = "Test2" } } };
var serialized = serializeTest.SerializeToXml();
var serialized2 = serializeTest2.SerializeToXml();
var deserialized = serialized.DeserializeXml<Test<Sub1>>();
var deserialized2 = serialized2.DeserializeXml<Test<Sub2>>();
Console.WriteLine(serialized);
Console.WriteLine();
Console.WriteLine(serialized2);
Console.ReadLine();
}
}
And my Serialize and DeSerialize extension methods:
public static string SerializeToXml<T>(this T valueToSerialize, string namespaceUsed = null)
{
var ns = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, (namespaceUsed != null) ? namespaceUsed : string.Empty) });
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(typeof(T));
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
You don't need to specify the XmlElement name when the properties match the XML. A few solutions, some kinda hacky :).
HACKY: use regex string replace to replace <I#> and </I#> to
just <I> and </I>
SOMEWHAT HACKY: This might work for you:
How to deserialize an XML array containing multiple types of elements in C#,
but you'd have to add an attribute for i0, i1 ... i100, etc.
BEST: Is that your entire XML? I'd honestly just use LINQToXml and
do a Descendants("Path") and get an array of strings back with 1 line of code. Serialization is not really the best solution for this.

Multiple, identical elements in the same C# DTO

I am trying to create a WCF-service, which can return me an XML document looking something like this:
<alert>
<identifier>SecretID</identifier>
<info>
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info>
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</alert>
The DTO of this file would look something like this:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public List<Info> Info { get; set; }
}
[DataContract(Name = "info", Namespace = "")]
public class Info
{
[DataMember(Name = "valueName")]
public string ValueName { get; set; }
[DataMember(Name = "value")]
public string Value { get; set; }
}
However, when I try with the following:
var alert = new Alert()
{
Identifier = "SecretID",
Info = new List<Info>
{
new Info() {ValueName = "Name1", Value = "Info1"},
new Info() {ValueName = "Name2", Value = "Info2"},
}
}
I get:
<alert>
<identifier>SecretID</identifier>
<info>
<info xmlns="">
<valueName>Name1</valueName>
<value>Info1</value>
</info>
<info xmlns="">
<valueName>Name2</valueName>
<value>Info2</value>
</info>
</info>
</alert>
I don't need the extra <info> tag, and the namespace xmlns="" would be nice to have removed too. What should I do to get rid of it?
You can use dictionary or key value pairs as mentioned below:
[DataContract(Name = "alert", Namespace = "")]
public class Alert
{
[DataMember(Name = "identifier")]
public string identifier { get; set; }
[DataMember(Name = "info")]
public KeyValuePair<string, string> Info { get; set; }
}
var alert = new Alert()
{
Identifier = "SecretID",
Info = new KeyValuePair<string, string>()
}

XPath matches for a XML serialized DataContract class

I have a DataContract class MyDataContract. I am serializing it to a XML file. Later, at a totally different "place" in my application I am loading this file. There I want to verify to load only, if the content of the file matches special conditions. Let's say I store a person and the association to the person's vehicle assuming a person can own just one vehicle. :-)
Here the DataContract classes:
namespace Test.DataContracts
{
[DataContract]
public class MyDataContract
{
[DataMember]
public string Identifier { get; set; }
[DataMember]
public Common.Person Person { get; set; }
[DataMember]
public Common.Vehicle Vehicle { get; set; }
}
}
namespace Test.DataContracts.Common
{
[DataContract]
public class Person
{
[DataMember]
public Global.Gender Gender { get; set; }
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Vehicle
{
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Vendor { get; set; }
}
}
namespace Test.DataContracts.Global
{
[DataContract]
public class Gender
{
[DataMember]
public int Type { get; set; }
[DataMember]
public string Name { get; set; }
}
}
Results in the following serialized XML:
<?xml version="1.0" encoding="utf-8"?>
<MyDataContract xmlns="http://schemas.datacontract.org/2004/07/Test.DataContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Identifier>123456789</Identifier>
<Person xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Gender xmlns:b="http://schemas.datacontract.org/2004/07/Test.DataContracts.Global">
<b:Name>Female</b:Name>
<b:Type>0</b:Type>
</a:Gender>
<a:Info>She is a beautiful lady.</a:Info>
<a:Name>Jane Doe</a:Name>
</Person>
<Vehicle xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Info>A super great car.</a:Info>
<a:Name>Mustang 1983 Turbo GT</a:Name>
<a:Vendor>Ford</a:Vendor>
</Vehicle>
</MyDataContract>
Now I want to filter out only Female (Type = 0) persons that own any Ford (Vendor = Ford) vehicle. I tried the following, but it always results in false for the matches.
string path = #"c:\janedoe.xml";
var xmlDoc = new XmlDocument();
xmlDoc.Load(path);
XmlNodeList xNodes = xmlDoc.SelectNodes(#"//namespace::*[not(. = ../../namespace::*)]");
var xNamespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
foreach (XmlNode node in xNodes)
{
if (!string.IsNullOrWhiteSpace(xNamespaceManager.LookupNamespace(node.LocalName))) continue;
xNamespaceManager.AddNamespace(node.LocalName, node.Value);
}
using (var fs = new FileStream(path, FileMode.Open))
{
var xDocument = new XPathDocument(fs);
var xNavigator = xDocument.CreateNavigator();
XPathExpression exp1 = xNavigator.Compile(string.Format("MyDataContract/Person/Gender/Type/descendant::*[contains(text(), '{0}')]", "0"));
exp1.SetContext(xNamespaceManager);
XPathExpression exp2 = xNavigator.Compile(string.Format("MyDataContract/Vehicle/Vendor/descendant::*[contains(text(), '{0}')]", "Ford"));
exp2.SetContext(xNamespaceManager);
if (xNavigator.Matches(exp1) && xNavigator.Matches(exp2))
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
}
Can anyone help?
UPDATE 1 - I have changed the code using XmlNamespaceManager. But still results in false when executing xNavigator.Matches(exp1).
If you have XDocument it is easier to use LINQ-to-XML:
var xdoc = XDocument.Load(memorystream);
// Making it simple, grab the first
var type = xdoc.Descendants(XName.Get("Type","http://schemas.datacontract.org/2004/07/Test.DataContracts.Global")).FirstOrDefault();
var vendor = xdoc.Descendants(XName.Get("Vendor", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Common")).FirstOrDefault();
string path = "blah";
if (type != null && type.Value == "0" && vendor != null && vendor.Value == "Ford")
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
If you are sure that XPath is the only solution you need:
using System.Xml.XPath;
var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("a", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Global");
var name = document.XPathSelectElement("path", namespaceManager).Value;

XML Parsing to c# objects

I am trying to do a XML parser which will extract data from a website using REST service, the protocol for communication is HTTP, the data I get is in XML format, and I get to the data I need after several requests to different addresses on the server. I need to parse this data to c# objects so I can operate with them lately.
The information on the server is on 5 levels( I am willing to make work only 4 of them for know)
1-List of vendors
2-List of groups
3-List of subgroups
4-List of products
5-List of full information about products
After I get to 4th level I need to check if the product is in my database or if it has different details so I can add or update it.
With "GET" request to a server I get XML with this structure:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<vendors>
<vendor>
<id>someID</id>
<name>someName</name>
</vendor>
<vendor>
<id>someId1</id>
<name>somename1</name>
</vendor>
</vendors>
XML structure for groups is the same :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<groups vendor_id="43153185318">
<group>
<id>someID</id>
<name>someName</name>
</group>
<group>
<id>someId1</id>
<name>somename1</name>
</group>
The XML structure is analogical for subgroups and products, except that for products I have more elements like catalog_num, price etc.
I made the classes as follows :
public class VendorList
{
public List<Vendor> vendor_list { get; set; }
public VendorList()
{
vendor_list = new List<Vendor>();
}
}
public class Vendor
{
public string id { get; set; }
public string name { get; set; }
public List<Group> groups_list { get; set; }
public Vendor()
{
id = "N/A";
name = "N/A";
groups_list = new List<Group>();
}
}
public class Group
{
public string id { get; set; }
public string name { get; set; }
public List<SubGroup> subgroup_list { get; set; }
public Group()
{
id = "N/A";
name = "N/A";
subgroup_list = new List<SubGroup>();
}
}
public class SubGroup
{
public string id { get; set; }
public string name { get; set; }
public List<Product> product_list { get; set; }
public SubGroup()
{
id = "N/A";
name = "N/A";
product_list = new List<Product>();
}
}
public class Product
{
public string available { get; set; }
public string catalog_num { get; set; }
public string code { get; set; }
public string currency { get; set; }
public string description { get; set; }
public string haracteristics { get; set; }
public string product_id { get; set; }
public string model { get; set; }
public string name { get; set; }
public string price { get; set; }
public string price_dds { get; set; }
public string picture_url { get; set; }
public Product()
{
available = "N/A";
catalog_num = "N/A";
code = "N/A";
currency = "N/A";
description = "N/A";
haracteristics = "N/A";
product_id = "N/A";
model = "N/A";
name = "N/A";
price = "N/A";
price_dds = "N/A";
picture_url = "N/A";
}
}
and the Parser method like this :
public static void FillVendor(string url)
{
string result = GetXMLstream(url);
var vendors = new VendorList();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba.xml");
XDocument d = XDocument.Load(#"D:/proba/proba.xml");
vendors.vendor_list = (from c in d.Descendants("vendor")
select new Vendor()
{
id = c.Element("id").Value,
name = c.Element("name").Value
}).ToList<Vendor>();
foreach (Vendor v in vendors.vendor_list)
{
FillGroups(v.id);
}
}
public static void FillGroups(string vendorID)
{
string url = "main address" + vendorID;
string result = GetXMLstream(url);
var group = new Vendor();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba1.xml");
XDocument d = XDocument.Load(#"D:/proba/proba1.xml");
group.groups_list = (from g in d.Descendants("group")
select new Group()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<Group>();
foreach (Group g in group.groups_list)
{
FillSubGroup(vendorID, g.id);
}
}
public static void FillSubGroup(string vendorID, string groupID)
{
string url = "main address" + vendorID+"/"+groupID;
string result = GetXMLstream(url);
var subgroup = new Group();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
subgroup.subgroup_list = (from g in d.Descendants("subgroup")
select new SubGroup()
{
id = g.Element("id").Value,
name = g.Element("name").Value
}).ToList<SubGroup>();
foreach (SubGroup sb in subgroup.subgroup_list)
{
FillProduct(vendorID, groupID, sb.id);
}
}
public static void FillProduct(string vendorID,string groupID,string subgroupID)
{
string url = "main address" + vendorID + "/" + groupID+"/"+subgroupID;
string result = GetXMLstream(url);
var product = new SubGroup();
XmlDocument doc = new XmlDocument();
doc.Load(new StringReader(result));
doc.Save(#"D:/proba/proba2.xml");
XDocument d = XDocument.Load(#"D:/proba/proba2.xml");
product.product_list = (from g in d.Descendants("subgroup")
select new Product()
{
available = g.Element("available").Value,
catalog_num = g.Element("catalog_num").Value,
code = g.Element("code").Value,
currency = g.Element("currency").Value,
description = g.Element("description").Value,
haracteristics = g.Element("haracteristics").Value,
product_id = g.Element("id").Value,
model = g.Element("model").Value,
name = g.Element("name").Value,
price = g.Element("price").Value,
price_dds = g.Element("price_dds").Value,
picture_url = g.Element("small_picture").Value,
}).ToList<Product>();
}
But after finishing parsing I try to check if my Lists are populated with objects, but I get an error which says that they are null "NullReferenceException"
So my question is did I make classes properly and is my parsing method right ( you can suggest how to parse the xml without creating a file on my computer) and if I didn't where is my mistake and how should I make it work properly?
Thanks in advance!
modify this line add 's'( vendor -> vendors)
-> vendors.vendor_list = (from c in d.Descendants("vendor")
and the same case for group -> groups
Instead of making the classes yourself.
Create a properly formatted XML Schema either manually or with Visual Studio and then from that XSD File you've created generate your C# Classes.

Categories

Resources