datacontractserializer deserialize list<> always empty - c#

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.

Related

Soap WebService : Add attribute to an object based on a criteria

I have a Soap WS that returns an object
[DataContract(Namespace = Configuration.Namespace)]
public class GetAccountResponse
{ [DataMember]
public List<Account> accounts { get; set; }
}
[DataContract(Namespace = Configuration.Namespace)]
public class Account
{ [DataMember(Name = "GUIDAccount")]
public Guid? accountid { get; set; }
[DataMember]
public List<Contract> Contracts { get; set; }
}
[DataContract(Namespace = Configuration.Namespace)]
public struct Contract
{
[DataMember(Name = "IDContrat")]
public string contrat { get; set; }
[DataMember(Name = "Phone")]
public string phone { get; set; }
}
I need to add a new attribute to the contract,but only on certain request criteria .
[DataMember(Name = "state")]
public string state { get; set; }
Response :
//all the time
return new GetAccountResponse
{
accounts = myaccounts.Values.ToList()
};
//my request matches a critertia the obejcts with the new
attribute
if(//mycriteria)
return new GetAccountResponse //object with state attribute
{
accounts = myaccounts.Values.ToList()
};
How I can achieve this by using the same objects GetAccountResponse?
Maybe you can try setting the EmitDefaultValue property of the DataMemberAttribute to false.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-member-default-values?redirectedfrom=MSDN
E.g:
[DataContract]
class MyDC
{
[DataMember]
public string DM1;
[DataMember(EmitDefaultValue = false)]
public string DM2;
[DataMember]
public string DM3;
}
Then setting this property to null:
[OperationContact]
public MyDC GetMyDC()
{
MyDC mdc = new MyDC();
if (condition)
{
// Code to prevent DM2 from being deserialized
mdc.DM2 = null;
}
return mdc;
}
This way, that property doesn't get written to the output stream on serialization.
source: Can I prevent a specific datamember from being deserialized?

C# Mapping XML Response into Unknown Class

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

remove xmlns:i="http://www.w3.org/2001/XMLSchema-instance" when using DataContractSerializer

how can I remove the xmlns:i="http://www.w3.org/2001/XMLSchema-instance" when using DataContractSerializer.
this is what I'm getting:
<ProfileModel xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Email>wolverine#wolverine.com</Email>
<FirstName>wolverine</FirstName>
<ID>ty1002225</ID>
<LastName>wolverine3</LastName>
<PhoneNumber>66332214477</PhoneNumber>
<SourceSystem>TY</SourceSystem>
</ProfileModel>
I want to get something like this:
<ProfileModel>
<Email>wolverine#wolverine.com</Email>
<FirstName>wolverine</FirstName>
<ID>ty1002225</ID>
<LastName>wolverine3</LastName>
<PhoneNumber>66332214477</PhoneNumber>
<SourceSystem>TY</SourceSystem>
</ProfileModel>
this is my model:
[DataContract(Namespace = "")]
public class CRMProfileModel
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public string Email { get; set; }
[DataMember]
public string PhoneNumber { get; set; }
[DataMember]
public string SourceSystem { get; set; }
[DataMember]
public string ID { get; set; }
}
I'm trying to avoid to use string replace to remove it.
how can I remove the xmlns:i="http://www.w3.org/2001/XMLSchema-instance" when using DataContractSerializer.
hii Romeo... i also tried for couple of hours to remove xmlns:i="http://www.w3.org/2001/XMLSchema-instance".
Finally i found my best,hope it will helpful
public IHttpActionResult Post([FromBody]MessageResponse value)
{
var messageresponse =new CRMProfileModel(){.....};
DataContractSerializer doc = new DataContractSerializer(messageresponse.GetType());
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, messageresponse);
var i = Encoding.UTF8.GetString(ms.ToArray());
var r = i.Replace("xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"","");
var ss = new XmlDocument();
ss.LoadXml(r);
return Content(HttpStatusCode.OK, ss.DocumentElement, Configuration.Formatters.XmlFormatter);
}

error deserializing the object of type .... End element '......' from namespace '' expected. Found element 'item' from namespace ''

When I deserialize my jsonstring ,I am getting error message
There was an error deserializing the object of type RecordInfo. End element 'Warning' from namespace '' expected. Found element 'item' from namespace ''.
This is my JsonString
public const string jsonString = #"
{
""RequestId"":514106,
""Warning"":[],
""CustomerData"": {
""Email"":""abc#abc.com"",
""FullName"":""OrTguOfE"",
""OrderData"":[]
}
}";
Data contracts
[DataContract]
public class RecordInfo
{
[DataMember(Name = "RequestId")]
public string RequestId { get; set; }
[DataMember(Name = "Warning")]
public string Warning { get; set; }
[DataMember(Name = "CustomerData")]
public CustomerData CustomerData { get; set; }
}
[DataContract]
public class CustomerData
{
[DataMember(Name = "Email")]
public string RequestId { get; set; }
[DataMember(Name = "FullName")]
public string FullName { get; set; }
[DataMember(Name = "OrderData")]
public OrderData[] OrderData { get; set; }
}
[DataContract]
public class OrderData
{
[DataMember(Name = "OrderId")]
public string OrderId { get; set; }
[DataMember(Name = "SourceId")]
public string SourceId { get; set; }
[DataMember(Name = "SourceData")]
public SourceData[] SourceData { get; set; }
}
[DataContract]
public class SourceData
{
[DataMember(Name = "SourceDescription")]
public string SourceDescription { get; set; }
[DataMember(Name = "ProductName")]
public string ProductName { get; set; }
}
}
This is the Deserializer I use
private static T Deserialize<T>(string jsonString)
{
using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
{
var serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
When I deserialize the bove jsonstring ,I am getting error message
There was an error deserializing the object of type RecordInfo. End element 'Warning' from namespace '' expected. Found element 'item' from namespace ''.
Any suggestions to resolve this error?
Set IsRequired = false, e.g.:
[DataMember(Name = "RequestId", IsRequired = false)]
MSDN Source: DataMemberAttribute.IsRequired Property
Gets or sets a value that instructs the serialization engine that the
member must be present when reading or deserializing.
Another reason I found for the similar error is when we map an array type of Json field to a non array field of the data contract class. (e.g.) my JSON data was like -
"ipAddress": [
"10.61.255.25",
"fe80:0000:0000:0000:10e1:0b66:96a6:9ac8"
]
But because I was unaware of this type of IPAddress data, I was mapping ipaddress to
[DataMember(Name="ipAddress")]
public string IPAddress ( get; set; }
Instead, it should be
[DataMember(Name="ipAddress")]
public string[] IPAddress ( get; set; }

I need to know how to deserialize a specific XML into objects defined in a custom class in C#

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.

Categories

Resources