I tried to create a method in a ApiController that looks like this:
public DemoList<Demo> GetAll()
{
var result = new DemoList<Demo>() { new Demo(){Y=2}, new Demo(), new Demo(){Y=1} };
result.Name = "Test";
return result;
}
Demo and DemoList look like this:
public interface INamedEnumerable<out T> : IEnumerable<T>
{
string Name { get; set; }
}
public class Demo
{
public int X { get { return 3; } }
public int Y { get; set; }
}
public class DemoList<T> : List<T>, INamedEnumerable<T>
{
public DemoList()
{
}
public string Name { get; set; }
}
I then cheked the ouput with fiddler
GET http://localhost:8086/api/Demo
and got the following:
XML (Accept header set to application/xml)
<ArrayOfDemo xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/XXX.WebAPI"><Demo><Y>2</Y></Demo><Demo><Y>0</Y></Demo><Demo><Y>1</Y></Demo></ArrayOfDemo>
JSON (Accept header set to application/json)
[{"X":3,"Y":2},{"X":3,"Y":0},{"X":3,"Y":1}]
My question is quite simple: Why is the X variable not serialized with the XML version (I thought that readonly properties were serialized) and more important, why in both cases is the Name property (which is writable) not serialized??
What are the alternatives to make this work like I expected?
Edit:
Please, note that I'm in a WebAPI context! By default, the XmlSerializer is automatically set to XmlMediaTypeFormatter and the JSONSerializer to JsonMediaTypeFormatter
This seems to be a bug... using the following workaround made the trick:
public class ListWrapper<T>
{
public ListWrapper(INamedEnumerable<T> list)
{
List = new List<T>(list);
Name = list.Name;
}
public List<T> List { get; set; }
public string Name { get; set; }
}
XML serializers only allows serialization of properties with "set" provided.
What are you using to serialize it? If you don't need attributes you could use DataContractSerializer as mentioned here. By default properties without a set are not serialized however using DataContractSerializer or implementing IXmlSerializable should do the trick for you.
using System;
using System.Runtime.Serialization;
using System.Xml;
[DataContract]
class MyObject {
public MyObject(Guid id) { this.id = id; }
[DataMember(Name="Id")]
private Guid id;
public Guid Id { get {return id;}}
}
static class Program {
static void Main() {
var ser = new DataContractSerializer(typeof(MyObject));
var obj = new MyObject(Guid.NewGuid());
using(XmlWriter xw = XmlWriter.Create(Console.Out)) {
ser.WriteObject(xw, obj);
}
}
}
I have a class, NetworkClient as a base class :
using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;
namespace Network
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class NetworkClient
{
public NetworkClient()
{
tcpClient = new TcpClient();
}
public NetworkClient(TcpClient client)
{
tcpClient = client;
}
public virtual bool IsConnected
{
get;
private set;
}
private StreamWriter writer { get; set; }
private StreamReader reader { get; set; }
private TcpClient tcpClient
{
get;
set;
}
public virtual NetworkServerInfo NetworkServerInfo
{
get;
set;
}
public async virtual void Connect(NetworkServerInfo info)
{
if (tcpClient == null)
{
tcpClient=new TcpClient();
}
await tcpClient.ConnectAsync(info.Address,info.Port);
reader = new StreamReader(tcpClient.GetStream());
writer = new StreamWriter(tcpClient.GetStream());
}
public virtual void Disconnect()
{
tcpClient.Close();
reader.Dispose();
writer.Dispose();
}
public async virtual void Send(string data)
{
await writer.WriteLineAsync(data);
}
public async virtual Task<string> Receive()
{
return await reader.ReadLineAsync();
}
}
}
And also have a child class derived from NetworkClient :
using System.Net;
namespace Network
{
using Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class SkyfilterClient : NetworkClient
{
public virtual IPAddress Address
{
get;
set;
}
public virtual int Port
{
get;
set;
}
public virtual string SessionID
{
get;
set;
}
public virtual User UserData
{
get;
set;
}
protected virtual bool Authenticate(string username, string password)
{
throw new System.NotImplementedException();
}
}
}
The problem is, that when im trying to cast NetworkClient into SkyfilterClient. An exception is thrown, Unable to cast object of type 'Network.NetworkClient' to type 'Network.SkyfilterClient'.
Whats wrong with my code ? I see that Stream can be converted to NetworkStream, MemoryStream. Why NetworkClient can't be converted to Skyfilter Client?
As long as the object is actually a SkyfilterClient, then a cast should work. Here is a contrived example to prove this:
using System;
class Program
{
static void Main()
{
NetworkClient net = new SkyfilterClient();
var sky = (SkyfilterClient)net;
}
}
public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}
However, if it is actually a NetworkClient, then you cannot magically make it become the subclass. Here is an example of that:
using System;
class Program
{
static void Main()
{
NetworkClient net = new NetworkClient();
var sky = (SkyfilterClient)net;
}
}
public class NetworkClient{}
public class SkyfilterClient : NetworkClient{}
HOWEVER, you could create a converter class. Here is an example of that, also:
using System;
class Program
{
static void Main()
{
NetworkClient net = new NetworkClient();
var sky = SkyFilterClient.CopyToSkyfilterClient(net);
}
}
public class NetworkClient
{
public int SomeVal {get;set;}
}
public class SkyfilterClient : NetworkClient
{
public int NewSomeVal {get;set;}
public static SkyfilterClient CopyToSkyfilterClient(NetworkClient networkClient)
{
return new SkyfilterClient{NewSomeVal = networkClient.SomeVal};
}
}
But, keep in mind that there is a reason you cannot convert this way. You may be missing key information that the subclass needs.
Finally, if you just want to see if the attempted cast will work, then you can use is:
if(client is SkyfilterClient)
cast
I'm surprised AutoMapper hasn't come up as an answer.
As is clear from all the previous answers, you cannot do the typecast. However, using AutoMapper, in a few lines of code you can have a new SkyfilterClient instantiated based on an existing NetworkClient.
In essence, you would put the following where you are currently doing your typecasting:
using AutoMapper;
...
// somewhere, your network client was declared
var existingNetworkClient = new NetworkClient();
...
// now we want to type-cast, but we can't, so we instantiate using AutoMapper
AutoMapper.Mapper.CreateMap<NetworkClient, SkyfilterClient>();
var skyfilterObject = AutoMapper.Mapper.Map<SkyfilterClient>(existingNetworkClient);
Here's a full-blown example:
public class Vehicle
{
public int NumWheels { get; set; }
public bool HasMotor { get; set; }
}
public class Car: Vehicle
{
public string Color { get; set; }
public string SteeringColumnStyle { get; set; }
}
public class CarMaker
{
// I am given vehicles that I want to turn into cars...
public List<Car> Convert(List<Vehicle> vehicles)
{
var cars = new List<Car>();
AutoMapper.Mapper.CreateMap<Vehicle, Car>(); // Declare that we want some automagic to happen
foreach (var vehicle in vehicles)
{
var car = AutoMapper.Mapper.Map<Car>(vehicle);
// At this point, the car-specific properties (Color and SteeringColumnStyle) are null, because there are no properties in the Vehicle object to map from.
// However, car's NumWheels and HasMotor properties which exist due to inheritance, are populated by AutoMapper.
cars.Add(car);
}
return cars;
}
}
If you HAVE to, and you don't mind a hack, you could let serialization do the work for you.
Given these classes:
public class ParentObj
{
public string Name { get; set; }
}
public class ChildObj : ParentObj
{
public string Value { get; set; }
}
You can create a child instance from a parent instance like so:
var parent = new ParentObj() { Name = "something" };
var serialized = JsonConvert.SerializeObject(parent);
var child = JsonConvert.DeserializeObject<ChildObj>(serialized);
This assumes your objects play nice with serialization, obv.
Be aware that this is probably going to be slower than an explicit converter.
In OOP, you can't cast an instance of a parent class into a child class. You can only cast a child instance into a parent that it inherits from.
I don't think you can downcast an object, however there is a simple way to "downcast" the object outside the box. It isn't type safe, but it works. First serialize the object into json, then deserialize it into the child class object. It works the same as if you were passing the object between apis. So, while there are some people who may say "this doesn't work or isn't good", I would argue that it is exactly the way our internet currently works, so... why not use that method? No mapping required as long as parameter names are the same, and they will be if it is a child class. Note: This will likely not copy any private fields; if you have a constructor with parameters, this probably needs to be tested as well to ensure there aren't side effects.
Here's my toolbox:
public static string ConvertToJson<T>(this T obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T ConvertToObject<T>(this string json)
{
if (string.IsNullOrEmpty(json))
{
return Activator.CreateInstance<T>();
}
return JsonConvert.DeserializeObject<T>(json);
}
Here's how to use it:
var sfcl = networkClient.ConvertToJson().ConvertToObject<SkyfilterClient>();
There's a few ways of doing this. However, here is one of the easiest ways to do this and it's reusable.
What is happening is that we're getting all the properties of the parent class and updating those same properties on the child class. Where baseObj would be the parent object and T would be the child class.
public static T ConvertToDerived<T>(object baseObj) where T : new()
{
var derivedObj = new T();
var members = baseObj.GetType().GetMembers();
foreach (var member in members)
{
object val = null;
if (member.MemberType == MemberTypes.Field)
{
val = ((FieldInfo)member).GetValue(baseObj);
((FieldInfo)member).SetValue(derivedObj, val);
}
else if (member.MemberType == MemberTypes.Property)
{
val = ((PropertyInfo)member).GetValue(baseObj);
if (val is IList && val.GetType().IsGenericType)
{
var listType = val.GetType().GetGenericArguments().Single();
var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(listType));
foreach (var item in (IList)val)
{
list.Add(item);
}
((PropertyInfo)member).SetValue(baseObj, list, null);
}
if (((PropertyInfo)member).CanWrite)
((PropertyInfo)member).SetValue(derivedObj, val);
}
}
return derivedObj;
}
You can't downcast. If the parent object is created, it cannot be cast to the child.
One suggested workaround would be to Create an interface which the parent implements. Have the child override functionality if needed or just expose the parents functionality. Change the cast to be an interface and do the operations.
Edit: May be could also check if the object is a SkyfilterClient using is keyword
if(networkClient is SkyfilterClient)
{
}
You can copy value of Parent Class to a Child class. For instance, you could use reflection if that is the case.
You can use the as operator to perform certain types of conversions between compatible reference types or nullable types.
SkyfilterClient c = client as SkyfilterClient;
if (c != null)
{
//do something with it
}
NetworkClient c = new SkyfilterClient() as NetworkClient; // c is not null
SkyfilterClient c2 = new NetworkClient() as SkyfilterClient; // c2 is null
I would recommend identifying the functionality you need from any subclasses, and make a generic method to cast into the right subclass.
I had this same problem, but really didn't feel like creating some mapping class or importing a library.
Let's say you need the 'Authenticate' method to take behavior from the right subclass. In your NetworkClient:
protected bool Authenticate(string username, string password) {
//...
}
protected bool DoAuthenticate<T>(NetworkClient nc, string username, string password) where T : NetworkClient {
//Do a cast into the sub class.
T subInst = (T) nc;
return nc.Authenticate(username, password);
}
You can't upcast you can only downcast Derived Class to Base Class
, but what you want to probably do is "create" not "cast" a new derived object from an existing base class object - just do it in the derived class's constructor
public class BaseClass
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
}
public class DerivedClass : BaseClass
{
public DerivedClass(BaseClass myBaseClassObject)
{
this.Property1 = myBaseClassObject.Property1;
this.Property2 = myBaseClassObject.Property2;
this.Property3 = myBaseClassObject.Property3;
}
public int DerivedClassProperty3 { get; set; }
public int DerivedClassProperty4 { get; set; }
public int DerivedClassProperty5 { get; set; }
}
DerivedClass myNewDerivedObject = new DerivedClass(baseClassObject);
Use the cast operator, as such:
var skyfilterClient = (SkyfilterClient)networkClient;
I realize it has been well-established elsewhere that serializing your NHibernate domain objects is generally a bad idea. My question here is in trying to understand how BinaryFormatter works, and why the scenario below yields the InvalidCastException.
The class structure roughly looks like this:
[Serializable]
public class Parent
{
public virtual Child child{get; set;}
}
[Serializable]
public class Child
{
public virtual ICollection<GrandChild> GrandChildren { get; set; }
}
[Serializable]
public class GrandChild
{
public virtual Pet pet{get; set;}
}
[Serializable]
public class Pet
{
public virtual IList<Toy> Toys { get; set; }
}
[Serializable]
public class Toy
{
public string ToyName { get; set; }
}
The serialization method looks like this:
public static byte[] Serialize(this object t)
{
using (var ms = new MemoryStream())
{
BinarySerializer.Serialize(ms, t);
return ms.ToArray();
}
}
Sometimes when calling Serialization e.g.
Parent p = new Parent() ....;
p.Serialize();
I will get
Unable to cast object of type 'NHibernate.Collection.Generic.PersistentGenericBag`1[Toy]' to type 'System.IConvertible'.
(all collections are mapped with bag semantics).
Even NHibernate.Collection.Generic.PersistentGenericBag<T> is marked [Serializable]
So given that everything here is marked as [Serializable] why would BinaryFormatter be attempting to cast PersistentGenericBag to an IConvertible in the first place?
Edit: In case it's relevant, this is under .NET 3.5 and NHibernate 3.1.0
By having the Pet class inherit from System.Runtime.Serialization.ISerializable, we now have complete control over how the Pet Class, and its members, in this case Toy, are serialized and de-serialized. Please see System.Runtime.Serialization.ISerializable, for more information about implementing System.Runtime.Serialization.ISerializable.
The sample below will serialize and then de-serialize an instance of the Parent class into a byte array and back again.
The public method, public GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), is called when the Pet type is being serialized; first we add an Int32 value indicating the number of items in the Toys list. Then we add each Toy from the list.
The protected constructor, protected Pet(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), is called when this type is being de-serialized. First we read the number of items that were stored in the Toys list, then use that to read each Toy instance from the serialized stream.
Please notice that for each Toy instance added to the serialized stream, we give it a different name; in this case we simply appended the value of the index to the word Toy; i.e. "Toy1", "Toy2",... This is because each item in the serialized stream needs a unique name. See: System.Runtime.Serialization.ISerializable.
And by controlling the serialization/de-serialization of the Toys list in Pet, we can eliminate the problem of not being able to serialize/de-serialize the list based on the type: NHibernate.Collection.Generic.PersistentGenericBag.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
static class Program
{
static void Main(string[] args)
{
Parent p = new Parent();
p.child = new Child();
p.child.GrandChildren = new List<GrandChild>();
p.child.GrandChildren.Add(new GrandChild { pet = new Pet() });
p.child.GrandChildren.First().pet.Toys = new List<Toy>();
p.child.GrandChildren.First().pet.Toys.Add(new Toy { ToyName = "Test" });
byte[] result = Serialize(p);
Parent backAgain = Deserialize(result);
}
public static System.Runtime.Serialization.Formatters.Binary.BinaryFormatter BinarySerializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
public static byte[] Serialize(Parent p)
{
using (var ms = new System.IO.MemoryStream())
{
BinarySerializer.Serialize(ms, p);
return ms.ToArray();
}
}
public static Parent Deserialize( byte[] data)
{
using (var ms = new System.IO.MemoryStream(data))
{
return (Parent)BinarySerializer.Deserialize(ms);
}
}
}
[Serializable]
public class Parent
{
public virtual Child child { get; set; }
}
[Serializable]
public class Child
{
public virtual ICollection<GrandChild> GrandChildren { get; set; }
}
[Serializable]
public class GrandChild
{
public virtual Pet pet { get; set; }
}
[Serializable]
public class Pet : System.Runtime.Serialization.ISerializable
{
public Pet() { }
// called when de-serializing (binary)
protected Pet(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
{
Toys = new List<Toy>();
int counter = info.GetInt32("ListCount");
for (int index = 0; index < counter; index++)
{
Toys.Add((Toy)info.GetValue(string.Format("Toy{0}",index.ToString()),typeof(Toy)));
}
}
// called when serializing (binary)
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
{
info.AddValue("ListCount", Toys.Count);
for (int index = 0; index < Toys.Count; index++)
{
info.AddValue(string.Format("Toy{0}", index.ToString()), Toys[index], typeof(Toy));
}
}
public virtual IList<Toy> Toys { get; set; }
}
[Serializable]
public class Toy
{
public string ToyName { get; set; }
}
If i have objects with properties of type object or objects that are generics, how can i serialize this?
Eg.
public class MyClass
{
public Object Item { get; set; }
}
or
public class MyClass<T>
{
public T Item { get; set; }
}
EDIT
My Generic class now looks like this:
public class MyClass<T>
{
public MySubClass<T> SubClass { get; set; }
}
public class MySubClass<T>
{
public T Item { get; set; }
}
Additonal question: How can i change the element name for Item at runtime to typeof(T).Name?
Have you tried the [Serializable] attribute?
[Serializable]
public class MySerializableClass
{
public object Item { get; set; }
}
[Serializable]
public class MySerializableGenericClass<T>
{
public T Item { get; set; }
}
Although the generic class is only serializable if the generic type parameter is serializable as well.
Afaik there is no way to constrain the type parameter to be serializable. But you can check at runtime using a static constructor:
[Serializable]
public class MySerializableGenericClass<T>
{
public T Item { get; set; }
static MySerializableGenericClass()
{
ConstrainType(typeof(T));
}
static void ConstrainType(Type type)
{
if(!type.IsSerializable)
throw new InvalidOperationException("Provided type is not serializable");
}
}
Use these methods to serialize\deserialize any object (even generics) to an XML file, though can be modified to suit other purposes:
public static bool SerializeTo<T>(T obj, string path)
{
XmlSerializer xs = new XmlSerializer(obj.GetType());
using (TextWriter writer = new StreamWriter(path, false))
{
xs.Serialize(writer, obj);
}
return true;
}
public static T DeserializeFrom<T>(string path)
{
XmlSerializer xs = new XmlSerializer(typeof(T));
using (TextReader reader = new System.IO.StreamReader(path))
{
return (T)xs.Deserialize(reader);
}
}
Let's assume I want to deserialize this (I've removed the namespaces to make things simpler):
<TextField>
<Caption>Location</Caption>
<Name>Location</Name>
</TextField>
TextField inherits from FormField, so in my class definition of FormField looks something like this:
[KnownType(typeof(TextField))]
[DataContract(Name = "FormField"]
public abstract class FormField
{
[DataMember]
public string Name { get; set; }
}
TextField class looks like this:
[DataContract(Name = "TextField")]
public class TextField : FormField
{
[DataMember]
public string Caption { get; set; }
}
I tried deserializing using this:
internal static FormField Deserialize(string xml)
{
var serializer = new DataContractSerializer(typeof(FormField)});
using (var backing = new StringReader(xml))
{
using (var reader = new XmlTextReader(backing))
{
return serializer.ReadObject(reader) as FormField;
}
}
}
I get a SerializationException: "Expecting element 'FormField'..."
Shouldn't your model look like this then?
public abstract class TextField: FormField
{
[DataMember]
public string Name { get; set; }
}
just a stab, I actually just started diving into the DataContractSerializer stuff list night oddly enough.
To solve my problem I added a container node to the XML, so that it looks like this:
<FormFieldContainer>
<TextField>
<Caption>Location</Caption>
<Name>Location</Name>
</TextField>
</FormFieldContainer>
I created this class:
[DataContract(Name = "FormFieldContainer")]
internal class FormFieldContainer
{
[DataMember]
internal FormField FormField { get; set; }
}
And my deserialize method looks like this:
internal static FormField Deserialize(string xml)
{
var serializer = new DataContractSerializer(typeof(FormFieldContainer));
using (var backing = new StringReader(xml))
{
using (var reader = new XmlTextReader(backing))
{
return ((FormFieldContainer)serializer.ReadObject(reader)).FormField;
}
}
}
If anyone has a better solution, please share it :)