I'm trying to achieve a generic solution for sending and receiving XML via any means(IP, serial textfiles, etc)
All appears to work fine until i get the response String.
What i need to do is cast this response sting into the correct class type (LoginResponse, LogoffResponse, CardPaymentResponse)
etc.
I cant get a generic way to cast this XMl back into the object in an efficient manner.
Heres what i have got so far:
Sample Response Strings:
XML for LoginResponse:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ServiceResponse RequestType="Login" ApplicationSender="POSsel01" WorkstationID="1" RequestID="1254" ProtocolVersion="000000001234" DeviceType="113" SWChecksum="AC3F" CommunicationProtocol="000000000432" Model="011" ApplicatioSoftwareVersion="000000000100" Manufacturer_Id="023" OverallResult="Success" xmlns="http://www.nrf-arts.org/IXRetail/namespace" xmlns:IFSF="http://www.ifsf.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace C:\Schema\ServiceResponse.xsd"/>
XML for LogoffResponse:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ServiceResponse RequestType="Logoff" ApplicationSender="POSsel01" WorkstationID="1" RequestID="1254" OverallResult="Success"
xmlns="http://www.nrf-arts.org/IXRetail/namespace" xmlns:IFSF="http://www.ifsf.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nrf-arts.org/IXRetail/namespace C:\Schema\ServiceResponse.xsd"/>
XML for CardPaymentResponse:
<CardServiceResponse
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
RequestType="CardPayment"
ApplicationSender="1"
WorkstationID="1"
RequestID="1254"
OverallResult="LoggedOut">
<Terminal
TerminalID="1234"
TerminalBatch="1"
STAN="55" />
<Tender>
<TotalAmount
Currency="EUR">0.00</TotalAmount>
</Tender>
</CardServiceResponse>
Code for Base class to contain common fields
public abstract class IFSFResponseBase
{
[XmlAttribute()]
public string RequestType { get; set; }
[XmlAttribute()]
public string ApplicationSender { get; set; }
[XmlAttribute()]
public string WorkstationID { get; set; }
[XmlAttribute()]
public string RequestID { get; set; }
[XmlAttribute()]
public string OverallResult { get; set; }
}
ServiceResponse Type, no additional fields here
public class ServiceResponse : IFSFResponseBase
{
}
CardServiceResponse Type, Common Fields here
public class CardServiceResponse : IFSFResponseBase
{
[XmlElement("Terminal")]
public CardServiceResponseTerminal Terminal
{ get; set; }
[XmlElement("Tender")]
public CardServiceResponseTender Tender
{
get; set;
}
}
CardServiceResponse Helper Classes
public class CardServiceResponseTerminal
{
[XmlAttribute()]
public string TerminalID { get; set; }
[XmlAttribute()]
public string TerminalBatchField { get; set; }
[XmlAttribute()]
public string STANField { get; set; }
}
public class CardServiceResponseTender
{
[XmlElement("TotalAmount")]
public CardServiceResponseTenderTotalAmount TotalAmount { get; set; }
}
public class CardServiceResponseTenderTotalAmount
{
private string valueField;
[XmlAttribute()]
public string CashBackAmount
{
get;
set;
}
[XmlAttribute()]
public string Currency
{
get;
set;
}
[XmlText()]
public string Value
{
get
{
return this.valueField;
}
set
{
this.valueField = value;
}
}
}
Concrete LogoffResponse Class, no additional fields
[XmlRoot("ServiceResponse")]
public class LogoffResponse : ServiceResponse
{
}
Concrete LoginResponse Class, extra fields handled
[XmlRoot("ServiceResponse")]
public class LoginResponse : ServiceResponse
{
[XmlAttribute()]
public string POPID { get; set; }
[XmlAttribute()]
public string ReferenceNumber { get; set; }
[XmlAttribute()]
public string ProtocolVersion { get; set; }
[XmlAttribute()]
public string DeviceType { get; set; }
[XmlAttribute()]
public string SWChecksum { get; set; }
[XmlAttribute()]
public string CommunicationProtocol { get; set; }
[XmlAttribute()]
public string Model { get; set; }
[XmlAttribute()]
public string ApplicatioSoftwareVersion { get; set; }
[XmlAttribute()]
public string Manufacturer_Id { get; set; }
}
Concrete CardPaymentResponse Class, no additional fields
[XmlRoot("CardServiceResponse")]
public class CardPaymentResponse : CardServiceResponse
{
}
Concrete CardPaymentRefundResponse Class, Some extra data needed
[XmlRoot("CardServiceResponse")]
public class CardPaymentRefundResponse : CardServiceResponse
{
[XmlAttribute()]
public string ExtraValue { get; set; }
}
Helper class to remove Name space from response
public class NamespaceIgnorantXmlTextReader : XmlTextReader
{
public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader) : base(reader) { }
public override string NamespaceURI
{
get { return ""; }
}
}
With the code below, I don't know the actual response I will receive until its parsed, so I don't
know what i can use in place of the concrete class (in this case ServiceResponse).
try
{
XmlSerializer serializer = new XmlSerializer(typeof(ServiceResponse));
StringReader sr = new StringReader(XMLResponse);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (ServiceResponse)serializer.Deserialize(XMLWithoutNamespace);
}
catch (Exception ex)
{
//Breakpoint code here for debugging ATM, To BE REMOVED
throw;
}
That is fine for Logoff Response type, but not if it was a LoginResponse Type.
So I could Use LoginResponse but then if the response is a CardServiceResponse this will fail with an exception
since the Root element is not SERVICERESPONSE but CardServiceResponse
try
{
XmlSerializer serializer = new XmlSerializer(typeof(LoginResponse));
StringReader sr = new StringReader(XMLResponse);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (LoginResponse)serializer.Deserialize(XMLWithoutNamespace);
}
catch (Exception ex)
{
//Breakpoint code here for debugging ATM, To BE REMOVED
throw;
}
I tried the following hack and it will work but I'm wondering if there is a better way to achieve this
private object InternalParse(string sXML)
{
object oRetVal = null;
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(sXML);
XmlNode ReqType = xmlDoc.DocumentElement.Attributes.GetNamedItem("RequestType");
Assembly asm = Assembly.GetExecutingAssembly();
Type type = asm.GetType(asm.GetName().Name + "." + ReqType.Value + "Response");
object instance = null;
try
{
instance = (object)Activator.CreateInstance(type);
}
catch
{
//problem creating type from RequestType Name + Response appended on, the class
//probably does not exist. Lets create the parent class
type = asm.GetType(asm.GetName().Name + "." + xmlDoc.DocumentElement.Name);
instance = (object)Activator.CreateInstance(type);
}
XmlSerializer serializer = new XmlSerializer(instance.GetType());
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
oRetVal = serializer.Deserialize(XMLWithoutNamespace);
}
catch (Exception ex)
{
//Log ex here
}
return oRetVal;
}
Thanks in Advance
Related
I have class structure as follows
public class Common
{
public int price { get; set; }
public string color { get; set; }
}
public class SampleProduct:Common
{
public string sample1 { get; set; }
public string sample2 { get; set; }
public string sample3 { get; set; }
}
I have XML file as follows
<ConfigData>
<Common>
<price>1234</price>
<color>pink</color>
</Common>
<SampleProduct>
<sample1>new</sample1>
<sample2>new</sample2>
<sample3>new123</sample3>
</SampleProduct>
</ConfigData>
Now I wanted to deserialize full XML data to SampleProduct object (single object).I can deserialize XML data to different object but not in a single object. Please help.
If you create the XML yourself Just do it like this:
<ConfigData>
<SampleProduct>
<price>1234</price>
<color>pink</color>
<sample1>new</sample1>
<sample2>new</sample2>
<sample3>new123</sample3>
</SampleProduct>
</ConfigData>
Otherwise, if you get the XML from other sources, do what Kayani suggested in his comment:
public class ConfigData
{
public Common { get; set; }
public SampleProduct { get; set; }
}
But don't forget that the SampleProduct created in this manner don't have "price" and "color" properties and these should be set using created Common instance
Here is the complete solution using the provided XML from you.
public static void Main(string[] args)
{
DeserializeXml(#"C:\sample.xml");
}
public static void DeserializeXml(string xmlPath)
{
try
{
var xmlSerializer = new XmlSerializer(typeof(ConfigData));
using (var xmlFile = new FileStream(xmlPath, FileMode.Open))
{
var configDataOSinglebject = (ConfigData)xmlSerializer.Deserialize(xmlFile);
xmlFile.Close();
}
}
catch (Exception e)
{
throw new ArgumentException("Something went wrong while interpreting the xml file: " + e.Message);
}
}
[XmlRoot("ConfigData")]
public class ConfigData
{
[XmlElement("Common")]
public Common Common { get; set; }
[XmlElement("SampleProduct")]
public SampleProduct XSampleProduct { get; set; }
}
public class Common
{
[XmlElement("price")]
public string Price { get; set; }
[XmlElement("color")]
public string Color { get; set; }
}
public class SampleProduct
{
[XmlElement("sample1")]
public string Sample1 { get; set; }
[XmlElement("sample2")]
public string Sample2 { get; set; }
[XmlElement("sample3")]
public string Sample3 { get; set; }
}
This will give you a single Object with all the elements you need.
I have the following XML:
<Envelope>
<Body>
<RESULT>
<SUCCESS>TRUE</SUCCESS>
<RecipientId>9876543210</RecipientId>
<ORGANIZATION_ID>12345-67890-b9e6bcd68d4fb511170ab3fcff55179d</ORGANIZATION_ID>
</RESULT>
</Body>
</Envelope>
Which I'm trying to deserialize to:
[XmlRoot(ElementName = "Envelope")]
public class Add_Recipent_response
{
public string Body { get; set; }
public string RESULT { get; set; }
public string SUCCESS { get; set; }
public string RecipientId { get; set; }
public string ORGANIZATION_ID { get; set; }
}
With this method:
protected void deserializeXML(string xmlResponse)
{
XmlSerializer deserializer = new XmlSerializer(typeof(Add_Recipent_response));
using (TextReader reader = new StringReader(xmlResponse))
{
try
{
Add_Recipent_response XmlData = (Add_Recipent_response)deserializer.Deserialize(reader);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetBaseException());
}
}
}
This throws an exception:
InnerException = {"Unexpected node type Element. ReadElementString
method can only be called on elements with simple or empty content.
Line 4, position 2."}
Can anyone tell me what I'm doing wrong?
Body and Result should be a classes as well because it contains elements. Something like
[XmlRoot(ElementName = "Envelope")]
public class Add_Recipent_response
{
public Body Body { get; set; }
}
public class Body
{
public Result RESULT { get; set; }
}
public class Result
{
public string SUCCESS { get; set; }
public string RecipientId { get; set; }
public string ORGANIZATION_ID { get; set; }
}
I'm deserializing data received from a web-service.
The problem is that the deserialization of List returns an empty list and no exception are generated. Can you help me figure out why?
We have tried several possible syntax. The code below is the closest to the correct solution but we cannot deserialize correctly as a list of classes.
<ArrayOfBatch xmlns="http://schemas.datacontract.org/2004/07/myns" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<MaasBatch>
<BatchName>All Users</BatchName>
<Users>
<MaasUsers>
<firstName>bob</firstName>
<lastName>thetest</lastName>
<sourceEmail>bob#source.com</sourceEmail>
<sourceTenantID>111</sourceTenantID>
<targetEmail>bob#target.com</targetEmail>
<targetTenantID>222</targetTenantID>
</MaasUsers>
</Users>
</MaasBatch>
</ArrayOfBatch>
Code:
List<MAASBatch> lstMaasBatches = null;
try
{
string target = string.Empty;
using (var response = request.GetResponse())
{
Stream streamReader = response.GetResponseStream();
DataContractSerializer serializer = new DataContractSerializer(typeof(List<MAASBatch>));
lstMaasBatches = (List<MAASBatch>)serializer.ReadObject(streamReader);
streamReader.Close();
}
return lstMaasBatches;
}
catch (Exception exc)
{
return lstMaasBatches;
}
Class:
[DataContract(Name = "MAASBatch", Namespace = "http://schemas.datacontract.org/2004/07/myns")]
[KnownType(typeof(MAASUsers))]
public class MAASBatch
{
[DataMember]
public string BatchName { get; set; }
[DataMember]
public List<MAASUsers> Users { get; set; }
[OnDeserializing]
internal void OnDeserializingCallBack(StreamingContext streamingContext)
{
this.Users = new List<MAASUsers>();
}
}
[DataContract(Name = "MAASUsers", Namespace = "http://schemas.datacontract.org/2004/07/myns")]
public class MAASUsers
{
[DataMember]
public string firstName { get; set; }
[DataMember]
public string lastName { get; set; }
[DataMember]
public string sourceEmail { get; set; }
[DataMember]
public int sourceAgentID { get; set; }
[DataMember]
public string targetEmail { get; set; }
[DataMember]
public int targetAgentID { get; set; }
}
Try to add Order and Name attribute to the Contract class.
Sample:
[DataMember(Order = 1, Name = "firstName")]
The data contract element name is "MAASUsers" but in the xml the element is named "MaasUsers". The data contract serializer is case sensitive so it will NOT match these two up.
The problem is XmlType("Erand") name is same as my class Erand, if I changed the class name to like Eranda it worked, is there any other way to say .net what to do?
I have an class
public class Erand
{
public long ID { get; set; }
public string AsjaNumber { get; set; }
}
and
[XmlType("Erand")]
public class ErandTsiv : Erand
{
[XmlElement("ID_KIS")]
public long idKis { get; set; }
[XmlElement("ID_ET")]
public long idEt { get; set; }
}
I want to deserialize ErandTsiv
from xml like
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfErand xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Erand>
<ID>573838383</ID>
<ID_KIS>573838383</ID_KIS>
<ID_ET></ID_ET>
<AsjaNumber>2-08-88785</AsjaNumber>
</Erand>
</ArrayOfErand>
like
var stream = new StringReader(erandid);
var serializer = new XmlSerializer(new List<ErandTsiv>().GetType());
var erandTsivs = (IList<ErandTsiv>)serializer.Deserialize(stream);`
but get an error
{"There was an error reflecting type 'System.Collections.Generic.List`1[Aet.test.unit.application.utility.TsivTapsustaTest.ErandTsiv]'."}
To deserialize this exact XML you should rely on a custom List definition for your List<ErandTsiv>, leaving out the XmlType on ErandTsiv
Your class definitions would then be :
public class Erand
{
public long ID { get; set; }
public string AsjaNumber { get; set; }
}
public class ErandTsiv : Erand
{
[XmlElement("ID_KIS")]
public long idKis { get; set; }
[XmlElement("ID_ET")]
public long idEt { get; set; }
}
[XmlRoot("ArrayOfErand")]
public class ErandTsivList
{
public ErandTsivList()
{
Erands = new List<ErandTsiv>();
}
[XmlElement("Erand")]
public List<ErandTsiv> Erands { get; set; }
}
And the deserialization would be :
var stream = new StringReader(x);
var serializer = new XmlSerializer(typeof(ErandTsivList));
var erandTsivs = (ErandTsivList)serializer.Deserialize(stream);
// your List<ErandTsiv> would then be in erandTsivs.Erands
Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<userAttributeList>
<attribute>
<userId>12345678</userId>
<attId>1234</attId>
<attName>Group</attName>
<attTypeId>8</attTypeId>
<attTypeName>User Group</attTypeName>
<attData>Member</attData>
</attribute>
<attribute>
<userId>12345678</userId>
<attId>1235</attId>
<attName>Contact Name</attName>
<attTypeId>16</attTypeId>
<attTypeName>Contact Center Greeting</attTypeName>
<attData>John Smith</attData>
</attribute>
...
</userAttributeList>
I want to deserialize it into the following classes:
[Serializable]
[XmlTypeAttribute(AnonymousType = true)]
public class UserAttributeList
{
[XmlArray(ElementName = "userAttributeList")]
[XmlArrayItem(ElementName = "attribute")]
public List<UserAttribute> attributes { get; set; }
public UserAttributeList()
{
attributes = new List<UserAttribute>();
}
}
[Serializable]
public class UserAttribute
{
public String userId { get; set; }
public String attId { get; set; }
public String attName { get; set; }
public String attTypeId { get; set; }
public String attTypeName { get; set; }
public String attData { get; set; }
}
Using the code below, where GetResponseStream() returns the XML object listed above:
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "userAttributeList";
xRoot.IsNullable = true;
XmlSerializer serializer = new XmlSerializer(typeof(UserAttributeList), xRoot);
try
{
return (UserAttributeList)serializer.Deserialize(request.GetResponse().GetResponseStream());
}
catch (Exception exc)
{
return null;
}
My code compiles with no errors, but the UserAttributeList that is returned shows no child "attribute" items. No errors are thrown
I would sooner do something like:
public class userAttributeList
{
[XmlElement]
public List<UserAttribute> attribute { get; set; }
public UserAttributeList()
{
attribute = new List<UserAttribute>();
}
}
public class UserAttribute
{
public int userId { get; set; }
public int attId { get; set; }
public string attName { get; set; }
public int attTypeId { get; set; }
public string attTypeName { get; set; }
public string attData { get; set; }
}
Personally I'd use LinqToXsd. Take the existing xml, generate an xsd from it then use LinqToXsd to load that xml into a LinqToXsd object. Then you can do things like:
xml.company.com.reports.report.Load(xmlFileContents);
to build a POCO.