So I have 2 classes. The first class is Message and the second one is MessageText class that derives from the Message class.
This is the Message class:
public class Message
{
public Message(Guid from, Guid to, MessageType type, DateTime sentAt)
{
From = from;
To = to;
Type = type;
SentAt = sentAt;
}
public Message()
{
}
public Guid From { get; set; }
public Guid To { get; set; }
public MessageType Type { get; set; }
public DateTime SentAt { get; set; }
}
This is the MessageText class:
public class MessageText : Message
{
public MessageText(Guid from, Guid to, MessageType type, DateTime sentAt, string text)
: base(from, to, type, sentAt)
{
this.Text = text;
}
public MessageText()
{
}
public string Text { get; set; }
}
This is how I do the serialization and deserialization:
public static byte[] ConvertToStream(object obj)
{
try
{
XmlSerializer bf = new XmlSerializer(obj.GetType());
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
catch { return new byte[0]; }
}
public static object ConvertFromStream<T>(byte[] data)
{
try
{
XmlSerializer bf = new XmlSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream(data))
{
return bf.Deserialize(ms);
}
}
catch { return null; }
}
How I use the methods:
Byte[] buffer = XMLHelper.ConvertToStream(message);
// Now send the message
// ...
/// When I receive the message
Message msg = (Message)XMLHelper.ConvertFromStream<Message>(data);
switch (msg.Type)
{
case EMessageType.Text:
if (OnMessageTextReceivedEvent != null)
{
MessageText msgT = (MessageText)XMLHelper.ConvertFromStream(data);
OnMessageTextReceivedEvent(msgT, EventArgs.Empty);
}
break;
//...
}
So when I receive the message I firstly want to deserialize it to the base class, so that I can see what type of message it is, and then to deserialize it to the correct type.
Note: I can use this approach without a problem if I want to deserialize to the correct type, for example if I deserialize a MessageText object to a MessageText object, but if I try to deserialize a MessageText object to its base class I get the following error: "There is an error in XML document (2, 2)."
Since the XmlSerializer expects the root element to be and the root element of the MessageText class is I just use the following code to change the root element of the MessageText class and now I can deserialize the object into a Message and/or MessageText class.
[XmlRoot("Message")]
public class MessageText : Message
{
public MessageText(Guid from, Guid to, MessageType type, DateTime sentAt, string text)
: base(from, to, type, sentAt)
{
this.Text = text;
}
public MessageText()
{
}
public string Text { get; set; }
}
Related
I have POCO which is as simple as
public partial class Member
{
public int ID { get; set; }
[Required]
[StringLength(100)]
public string MemberId { get; set; }
public DateTime CreatedOn { get; set; }
[Required]
[StringLength(100)]
public string FirstName { get; set; }}
and a Add command which looks like this
public class AddMemberCommand : ICommand
{
public AddMemberCommand(Member member )
{
ID = Guid.NewGuid();
MemberData = member;
}
public Member MemberData { get; private set; }
public Guid ID { get; }
public string CommandName
{
get { return "AddMemberCommand"; }
}
}
which inherits from
public interface ICommand
{
/// <summary>
/// Gets the command identifier.
/// </summary>
Guid ID { get; }
string CommandName { get; }
}
Now I send this code to a method which initializes Newton Json's serializing setting class with some parameters to return an object. The serializer looks like this
public class JsonTextSerializer
{
private readonly JsonSerializer _serializer;
public JsonTextSerializer()
{
_serializer = JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple
});
}
public void Serialize(TextWriter writer, object graph)
{
var jsonWriter = new JsonTextWriter(writer);
jsonWriter.Formatting = Formatting.Indented;
_serializer.Serialize(jsonWriter, graph);
writer.Flush();
}
public object Deserialize(TextReader reader)
{
var jsonReader = new JsonTextReader(reader);
try
{
return this._serializer.Deserialize(jsonReader);
}
catch (JsonSerializationException e)
{
// Wrap in a standard .NET exception.
throw new SerializationException(e.Message, e);
}
}
}
The serializer is used to convert the command into a payload for the brokered message as shown below
private BrokeredMessage CreateMessage(POCOS.Member member)
{
var serializer = new JsonTextSerializer();
var command = new AddMemberCommand(member);
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
serializer.Serialize(writer, command);
stream.Position = 0;
BrokeredMessage message = new BrokeredMessage(stream, true);
return message;
}
and there is a another method which receives the method
private POCOS.Member GetPocoFromMessage(BrokeredMessage message)
{
ITextSerializer serializer = new JsonTextSerializer();
AddMemberCommand command;
using (var stream = message.GetBody<Stream>())
{
using (var reader = new StreamReader(stream))
{
var payload = serializer.Deserialize(reader);
command = payload as AddMemberCommand;
}
}
return command.MemberData;
}
The issue is on deserializing some properties ( ID, CommandName) are filled with value except for MemberData which is null.
I can read the stream (by doing a reader.ReadToEnd()) and see it was transferred over the wire but Json can't deserialize all its object
At one time I also thought it perhaps picks only fields in the Interface but that's not the case
Your MemberData property has a private setter. Since the serializer needs access to the property externally, this setter should be public.
How can I set up the System.Runtime.Serialization serializer to ignore null values?
Or do I have to use the XmlSerializer for that? If so, how?
(I don't want <ecommerceflags i:nil="true"/> tags like this to be written, if it's null then just skip it)
With System.Runtime.Serialization.DataContractSerializer you need to mark the property with [DataMember(EmitDefaultValue = false)].
Example, the code below:
class Program
{
static void Main()
{
Console.WriteLine(SerializeToString(new Person { Name = "Alex", Age = 42, NullableId = null }));
}
public static string SerializeToString<T>(T instance)
{
using (var ms = new MemoryStream())
{
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(ms, instance);
ms.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(ms))
{
return sr.ReadToEnd();
}
}
}
}
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
[DataMember(EmitDefaultValue = false)]
public int? NullableId { get; set; }
}
prints the following:
<Person xmlns="http://schemas.datacontract.org/2004/07/ConsoleApplication4" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Age>42</Age>
<Name>Alex</Name>
</Person>
Though it has less value (except that it makes the serialized stream shorter), you can customize your serialization to achieve this.
When using System.Runtime.Serialization, you can implement the ISerializable interface:
[Serializable]
public class MyClass: ISerializable
{
private string stringField;
private object objectField;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (stringField != null)
info.AddValue("str", stringField);
if (objectField != null)
info.AddValue("obj", objectField);
}
// the special constructor for deserializing
private MyClass(SerializationInfo info, StreamingContext context)
{
foreach (SerializationEntry entry in info)
{
switch (entry.Name)
{
case "str":
stringField = (string)entry.Value;
break;
case "obj":
objectField = entry.Value;
break;
}
}
}
}
When using XML serialization, you can implement the IXmlSerializable interface to customize the output in a similar way.
As far as I read, you could use the Specified-Feature
public int? Value { get; set; }
[System.Xml.Serialization.XmlIgnore]
public bool ValueSpecified { get { return this.Value != null; } }
To only write it if it's specified.
Another way would be
[System.Xml.Serialization.XmlIgnore]
private int? value;
public int Value { get { value.GetValueOrDefault(); } }
I'm trying to find out a solution for this, but I'm not sure if it's possible or not.
I have a base class, lets say
public class A
{
[XmlAttribute("Date")]
public DateTime Date {get;set;}
}
and a derived class:
public class B: A
{
[XmlAttribute("Date")]
public new String StringDate {get;set;}
}
I have to serialize a Xml.
The value of "Date" on the Xml, is String and in fact it's not a DateTime format string. But I use "A" for many other stuff so I cannot just change it to String without affecting other parts of the program. Sadly it is not an option.
So my idea is to create a derived class "B" who inherit everything of "A" and overrided the property Date to get it fill from the deserialization and then format it to DateTime.
I read about virtual or abstracts but I'm not acquainted with it and don't have any clue about it, if it is the solution maybe someone can guide me on the first steps.
Anyone can help me?
EDIT
XML:
<Vorgang Vorgang="HQHT8GTQ">
<Vertragsbeginn Vertragsbeginn="20140202" />
</Vorgang>
Class A:
[DataContract(Name = "Vorgang")]
[KnownType(typeof(Vorgang))]
public class Vorgang
{
[IgnoreDataMember]
public DateTime Vertragsbeginn { get; set; }
}
Class B:
public class VorgangOverride : UTILMD.Vorgang
{
private string datestring;
[XmlAttribute("Vertragsbeginn")]
public new String Vertragsbeginn {
get { return datestring; }
set
{
base.Vertragsbeginn = DateUtil.StringToDate(value, EDIEnums.Vertragsbeginn);
datestring = value;
}
}
}
Deserialization method:
private static VorgangOverride Deserialize (XmlNode inVorgang)
{
using (MemoryStream stm = new MemoryStream())
{
using (StreamWriter stw = new StreamWriter(stm))
{
stw.Write(inVorgang.OuterXml);
stw.Flush();
stm.Position = 0;
XmlRootAttribute xRoot = new XmlRootAttribute { ElementName = "Vorgang", IsNullable = true };
var serializer = new XmlSerializer(typeof(VorgangOverride), xRoot);
VorgangOverride podItem = (VorgangOverride) serializer.Deserialize(stm);
return podItem;
}
}
}
EDIT:
Solved using
[XmlRoot("Vorgang")]
public class VorgangOverride
{
public VorgangOverride()
{
}
#region Public Properties
public string datestring;
[XmlElement("Vertragsbeginn")]
public Vertragsbeginn VertragsbeginnAsString { get ; set ;}
#endregion
}
public class Vertragsbeginn
{
[XmlAttribute("Vertragsbeginn")]
public String vertragsbeginn { get; set; }
}
I found the solution:
[DataContract(Name = "Vorgang")]
[KnownType(typeof(Vorgang))]
public class Vorgang
{
[XmlIgnore] // use XmlIgnore instead IgnoreDataMember
public DateTime Vertragsbeginn { get; set; }
}
// this class map all elements from the xml that you show
[XmlRoot("Vorgang")] // to map the Xml Vorgang as a VorgangOverride instance
public class VorgangOverride : Vorgang
{
[XmlAttribute("Vorgang2")] // to map the Vorgang attribute
public string VorgangAttribute { get; set; }
[XmlElement(ElementName = "Vertragsbeginn")] // to map the Vertragsbeginn element
public Vertragsbeginn VertragsbeginnElement
{
get { return _vertragsbeginn; }
set
{
base.Vertragsbeginn = new DateTime(); // here I Assing the correct value to the DateTime property on Vorgan class.
_vertragsbeginn = value;
}
}
private Vertragsbeginn _vertragsbeginn;
}
// this class is used to map the Vertragsbeginn element
public class Vertragsbeginn
{
[XmlAttribute("Vertragsbeginn")] // to map the Vertragsbeginn attriubute on the Vertragsbeginn element
public string VertragsbeginnAttribute { get; set; }
}
later I say:
var string xmlContent =
#"<Vorgang Vorgang2=""HQHT8GTQ"">
<Vertragsbeginn Vertragsbeginn=""20140202"" />
</Vorgang>";
var a = Deserialize<VorgangOverride>(xmlContent);
and this is the method to Deserialize:
// method used to deserialize an xml to object
public static T Deserialize<T>(string xmlContent)
{
T result;
var xmlSerializer = new XmlSerializer(typeof(T));
using (TextReader textReader = new StringReader(xmlContent))
{
result = ((T)xmlSerializer.Deserialize(textReader));
}
return result;
}
You will not be able to override a property with an other class type.
The reason is Polymorphism. (more information: https://msdn.microsoft.com/en-us/library/ms173152.aspx)
You can cast the class B to class A. Which means the class B must have all the properties and methods class A has, too. But in your case class B would have a String rather than a Date called Date. Which is simply not possible.
I have a method that ts returning Array. I have problem with one class that cant convert the return type to array.
This is my method:
public Model.BaseType[] returnPayment_Gateway()
{
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
WebHeaderCollection headers = request.Headers;
var settings = new DataContractJsonSerializerSettings { EmitTypeInformation = EmitTypeInformation.Never };
MemoryStream stream1 = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Model.BaseType),settings);
Model.Payment_Gateway[] allRecords = null;
if (headers["ServiceAuthentication"] != null)
{
string ServiceAuthentication = headers["ServiceAuthentication"].ToString();
bool serviceAuth = Service_Authentication(ServiceAuthentication);
DAL.DataManager dal = new DAL.DataManager();
if (serviceAuth == true)
{
allRecords = dal.Get_Payment_Gateway();
}
}
else
{
// Create a new customer to return
return new Model.ReturnResponse() { StatusCode = 201, StatusDescription = "Authentication Fails" };
}
return allRecords;
}
My problem is in else part, not sure how I can convert Model.ReturnResponse() to array now I am getting this error:
cannot implicitly convert type ReturnResponse to Model.BaseType[]
in case you like to see my 3 classes:
This is Base class:
[Serializable]
[DataContract]
[KnownType(typeof(Payment_Gateway))]
[KnownType(typeof(ReturnResponse))]
public class BaseType
{
}
this is Payment_Gateway class:
[DataContract]
public class Payment_Gateway:BaseType
{
[DataMember]
public int Payment_Gateway_ID { get; set; }
[DataMember]
public string Payment_Gateway_Name { get; set; }
[DataMember]
public string Payment_Gateway_URL { get; set; }
[DataMember]
public string Payment_Gateway_Description { get; set; }
and this is ReturnResponse class:
[DataContract]
public class ReturnResponse:BaseType
{
[DataMember]
public int StatusCode { get; set; }
[DataMember]
public string StatusDescription { get; set; }
}
Instead of trying to return a single ReturnResponse:
return new Model.ReturnResponse()
{ StatusCode = 201, StatusDescription = "Authentication Fails" };
Return an array, with the single element in it, since that's what your method is expected to return:
return new[] {
new Model.ReturnResponse()
{ StatusCode = 201, StatusDescription = "Authentication Fails" }
}
Instead of the hierarchy you have designed here, it would be easier if you encapsulate your return data in a response object that indicate a response type (success or failure) and the resulting data.
that class could look like the following
public enum ResponseTypes
{
Success,
Failure
}
public class Response
{
public ResponseTypes ResponseType {get; set;}
public Exception Error {get;set;}
public BaseType[] Data {get;set;}
}
this way it works like an envelope to your actual data and provides state to the caller to know if the call succeeded or not, and maybe for what reason, before reading the data (result)
its the same way WCF acts under the hood (in SOAP, at least) that it surrounds your data contract with a message envelope/enclosure to add more metadata for the caller to interpret the call status.
I am trying to deserialize an XML String into my class which is derived from another class but I am having a problem in doing so, I am getting the following error:
{"The specified type is abstract: name='DeviceRequest', namespace='', at <DeviceRequest xmlns=''>."}
I assume i am missing some decorator attribute that will inform the serializer how to deserialize this xml into the class?
Here is my code:
//Usage
DeviceRequest dreq = DeviceRequest.ParseRequest(e.XML);
//Base Class
public abstract class IFSFRequestBase
{
private string m_ApplicationSender = "";
private int m_WorkStationID = 0;
private string m_RequestID = "";
[XmlAttribute()]
public abstract string RequestType { get; set; }
public abstract byte[] Message();
[XmlAttribute()]
public string ApplicationSender
{
get
{
return m_ApplicationSender;
}
set
{
m_ApplicationSender = value;
}
}
[XmlAttribute()]
public int WorkStationID
{
get
{
return m_WorkStationID;
}
set
{
m_WorkStationID = value;
}
}
[XmlAttribute()]
public string RequestID
{
get
{
return m_RequestID;
}
set
{
m_RequestID = value;
}
}
}
//Derived Class
public abstract class DeviceRequest : IFSFRequestBase
{
private string m_TerminalID = "";
private string m_SequenceID = "";
private Output m_OutputField = null;
[XmlAttribute(), DefaultValue("")]
public string TerminalID
{
get
{
return m_TerminalID;
}
set
{
m_TerminalID = value;
}
}
[XmlAttribute(), DefaultValue("")]
public string SequenceID
{
get
{
return m_SequenceID;
}
set
{
m_SequenceID = value;
}
}
[XmlElement("Output")]
public Output OutputField
{
get
{
return m_OutputField;
}
set
{
m_OutputField = value;
}
}
public static DeviceRequest ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(DeviceRequest));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (DeviceRequest)serializer.Deserialize(XMLWithoutNamespace);
}
}
// helper class to ignore namespaces when de-serializing
public class NamespaceIgnorantXmlTextReader : XmlTextReader
{
public NamespaceIgnorantXmlTextReader(System.IO.TextReader reader) : base(reader) { }
public override string NamespaceURI
{
get { return ""; }
}
}
UPDATE:
OK based on the answers here. I have updated the code, I now have a class Display that derives from DeviceRequest. I now get the following error:
{"There is an error in XML document (2, 2)."}, {"<DeviceRequest xmlns=''> was not expected."}
public class Display : DeviceRequest
{
public static Display ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(Display));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (Display)serializer.Deserialize(XMLWithoutNamespace);
}
[XmlAttribute()]
public override string RequestType
{
get { return "Output"; } set { }
}
public override byte[] Message()
{
throw new NotImplementedException();
}
}
DeviceRequest dreq = Display.ParseRequest(e.XML);
As DeviceRequest is an abstract type, it cannot be instantiated directly. It is impossible to directly deserialize into instances of Device-Request. That said, if there are some non-abstract classes derived from it, please show some of them.
OK, I have resolved this issue now. Thanks for the input guys.
I needed to add:
[System.Xml.Serialization.XmlRootAttribute(ElementName = "DeviceRequest")]
to the Display Class
[System.Xml.Serialization.XmlRootAttribute(ElementName = "DeviceRequest")]
public class Display : DeviceRequest
{
public static Display ParseRequest(string sXML)
{
XmlSerializer serializer = new XmlSerializer(typeof(Display));
StringReader sr = new StringReader(sXML);
NamespaceIgnorantXmlTextReader XMLWithoutNamespace = new NamespaceIgnorantXmlTextReader(sr);
return (Display)serializer.Deserialize(XMLWithoutNamespace);
}
[XmlAttribute()]
public override string RequestType
{
get { return "O"; } set { }
}
public override byte[] Message()
{
throw new NotImplementedException();
}
}