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>()
}
Related
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);
The output i want as below
<SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body >
</SOAP:Envelope >
The output xml that i am getting as my result
<?xml version="1.0"?>
<SOAP:Envelope xmlns:m="http://www.e-courier.com/schemas/" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body UserGUID="">
<m:SaveOrder >
<m:Order UserID="" Notes="" CustomerID="" />
</m:SaveOrder>
</SOAP:Body>
</SOAP:Envelope>
My XML Class code:
[XmlRoot(ElementName="Order")]
public class Order {
[XmlAttribute(AttributeName="UserID")]
public string UserID { get; set; }
[XmlAttribute(AttributeName="Notes")]
public string Notes { get; set; }
[XmlAttribute(AttributeName="CustomerID")]
public string CustomerID { get; set; }
}
[XmlRoot(ElementName="SaveOrder", Namespace="http://www.e-courier.com/schemas/")]
public class SaveOrder {
[XmlElement(ElementName="Order")]
public Order Order { get; set; }
[XmlAttribute(AttributeName="m", Namespace="http://www.w3.org/2000/xmlns/")]
public string M { get; set; }
}
[XmlRoot(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Body {
[XmlElement(ElementName="SaveOrder", Namespace="http://www.e-courier.com/schemas/")]
public SaveOrder SaveOrder { get; set; }
[XmlAttribute(AttributeName="UserGUID")]
public string UserGUID { get; set; }
}
[XmlRoot(ElementName="Envelope", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope {
[XmlElement(ElementName="Body", Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName="SOAP", Namespace="http://www.w3.org/2000/xmlns/")]
public string SOAP { get; set; }
}
My Code where i am generating xml
var SaveOrder = new ECSaveOrderRequest.Envelope
{
Body = new ECSaveOrderRequest.Body
{
UserGUID = guid,
SaveOrder = new ECSaveOrderRequest.SaveOrder
{
Order = new ECSaveOrderRequest.Order
{
UserID = Uid,
Notes = "",
CustomerID=""
}
}
}
};
var ns = new XmlSerializerNamespaces();
ns.Add("SOAP", "http://schemas.xmlsoap.org/soap/envelope/");
ns.Add("m", "http://www.e-courier.com/schemas/");
var ser = new XmlSerializer(typeof(ECSaveOrderRequest.Envelope));
using (var ms = new MemoryStream())
{
// write the DTO to the MemoryStream
ser.Serialize(ms, SaveOrder, ns);
using (var wc = new WebClient())
{
wc.Encoding = System.Text.Encoding.UTF8;
ms.Position = 0;
StreamReader stream = new StreamReader(ms);
string requestString = stream.ReadToEnd();
var resp = wc.UploadData(ECUrl, ms.ToArray());
}
}
You need to explicitly clear the xml namespace on SaveOrder.Order or the serializer will default to SaveOrder's xml namespace.
Here you go:
using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
namespace ECSaveOrderRequest
{
/*
* <SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body >
</SOAP:Envelope >*/
public class Order
{
[XmlAttribute(AttributeName = "UserID")]
public string UserID { get; set; }
[XmlAttribute(AttributeName = "Notes")]
public string Notes { get; set; }
[XmlAttribute(AttributeName = "CustomerID")]
public string CustomerID { get; set; }
}
public class SaveOrder
{
[XmlElement(ElementName = "Order", Namespace = "")]
public Order Order { get; set; }
}
public class Body
{
[XmlElement(ElementName = "SaveOrder", Namespace = "http://www.e-courier.com/schemas/")]
public SaveOrder SaveOrder { get; set; }
[XmlAttribute(AttributeName = "UserGUID")]
public string UserGUID { get; set; }
}
[XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public class Envelope
{
[XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
public Body Body { get; set; }
[XmlAttribute(AttributeName = "SOAP", Namespace = "http://www.w3.org/2000/xmlns/")]
public string SOAP { get; set; }
}
class Program
{
static void Main(string[] args)
{
var SaveOrder = new ECSaveOrderRequest.Envelope
{
Body = new ECSaveOrderRequest.Body
{
UserGUID = "{redacted}",
SaveOrder = new ECSaveOrderRequest.SaveOrder
{
Order = new ECSaveOrderRequest.Order
{
UserID = "1",
Notes = "Signature Requiered",
CustomerID = "3"
}
}
}
};
var ns = new XmlSerializerNamespaces();
ns.Add("SOAP", "http://schemas.xmlsoap.org/soap/envelope/");
ns.Add("m", "http://www.e-courier.com/schemas/");
var ser = new XmlSerializer(typeof(ECSaveOrderRequest.Envelope));
var ms = new MemoryStream();
// write the DTO to the MemoryStream
ser.Serialize(ms, SaveOrder, ns);
ms.Position = 0;
var xml = Encoding.UTF8.GetString(ms.GetBuffer());
Console.WriteLine(xml);
Console.ReadKey();
}
}
}
outputs
<?xml version="1.0"?>
<SOAP:Envelope xmlns:m="http://www.e-courier.com/schemas/" xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP:Body UserGUID="{redacted}">
<m:SaveOrder>
<Order UserID="1" Notes="Signature Requiered" CustomerID="3" />
</m:SaveOrder>
</SOAP:Body>
</SOAP:Envelope>
Which is a serialization of the same XML document as
<SOAP:Envelope xmlns:SOAP='http://schemas.xmlsoap.org/soap/envelope/' >
<SOAP:Body UserGUID = '{redacted}' >
<m:SaveOrder xmlns:m = 'http://www.e-courier.com/schemas/' >
<Order UserID = '1' Notes = 'Signature Requiered' CustomerID = '3' >
</Order >
</m:SaveOrder >
</SOAP:Body>
</SOAP:Envelope>
.
My below my code which generate an xml and return the generated xml but the current format is not the structure expected:
My Class:
public class response
{
[StringLength(64)]
public string reference { get; set; }
public int responseCode { get; set; }
[StringLength(140)]
public string responseMessage { get; set; }
[StringLength(32)]
public string transactionId { get; set; }
public List<account> accounts { get; set; }
}
public class account
{
[StringLength(64)]
public string account_number { get; set; }
}
rs = "<?xml version='1.0' encoding='UTF-8'?><USSDResponse><Status>true</Status><StatusMessage>Account details returned for 08069262257</StatusMessage><SessionID>31853F5C-A1C1-2A6F-E054-8E1F65C33B15</SessionID><AccountNumber><AccountNo>0003893369</AccountNo><AccountStatus>ACCOUNT OPEN REGULAR</AccountStatus><AvailableBalance>17674.69</AvailableBalance><AccountName>IYEKE IKECHUKWU I.</AccountName><AccountCurrency>NGN</AccountCurrency><ProductName>CURRENT STAFF</ProductName></AccountNumber><AccountNumber><AccountNo>0064612613</AccountNo><AccountStatus>ACCOUNT OPEN REGULAR</AccountStatus><AvailableBalance>201132.18</AvailableBalance><AccountName>IKECHUKWU ISRAEL IYEKE</AccountName><AccountCurrency>NGN</AccountCurrency><ProductName>HIDA</ProductName></AccountNumber></USSDResponse>";
x.LoadXml(rs);
status = x.GetElementsByTagName("Status")[0].InnerText;
SessionID = x.GetElementsByTagName("SessionID")[0].InnerText;
if (status != null && status == "true")
{
var accts = x.GetElementsByTagName("AccountNo");
var names = x.GetElementsByTagName("ProductName");
if (accts.Count >= 2)
{
//var AcctNo = new accounts();
foreach (XmlElement a in accts)
{
var acctNo = a.InnerText.Substring(0, 10);
accounts.Add(new account { account_number = acctNo });
}
o.accounts = accounts;
o.reference = reference;
o.responseCode = 6;
o.responseMessage = "Please you can only purchase airtime in naira only and no kobo inclusive.";
o.transactionId = "Nil";
logger.Info($"Wrong amount: {amountString} including kobo entered by the user for mobile number: {msisdn}");
return o;
}
}
Result:
<response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<reference>565695769-8490890112091</reference>
<responseCode>6</responseCode>
<responseMessage>Please you can only purchase airtime in naira only and no kobo inclusive.</responseMessage>
<transactionId>Nil</transactionId>
<accounts>
<account><account_number>0003893369</account_number></account>
<account><account_number>0064612613</account_number></account>
</accounts>
</response>
My above code generate an xml and return the generated xml but the current format is not the structure expected:But I want the result to be exactly with the removal of tag <account_number></account_number>:
<response xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<reference>565695769-8490890112091</reference>
<responseCode>6</responseCode>
<responseMessage>Please you can only purchase airtime in naira only and no kobo inclusive.</responseMessage>
<transactionId>Nil</transactionId>
<accounts>
<account>0003893369</account>
<account>0064612613</account>
</accounts>
</response>
Modify your class structure as below,
public class Accounts {
public Accounts ()
{
Account = new List<string>();
}
public List<string> Account { get; set; }
}
public class Response {
...
public Accounts Accounts { get; set; }
}
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
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;