DataContractSerializer and Known Types - c#

I am serializing an object in code (not via a WCF call), and I am getting a little hung up with known types (I have used them with WCF, but not with the DataContract serializer as a "stand-alone" serializer)
I get an exception when I run the code below. I expected it to run without error because I supplied Known Types. What am I getting wrong here?
public class Parent {}
public class Child: Parent {}
// the code -- serialized is a serialized Child
// type is typeof(Parent) (I know it works if I pass typeof(Child), but isn't that what Known Types is all about??
// passing the known types seems to make no difference -- this only works if I pass typeof(Child) as the first param
var serializer = new DataContractSerializer(parentType, new Type[] {typeof(Child)});
object test = serializer.ReadObject(serialized);

Ok, so I'm having one of those days where I keep answering myself. The problme was not with the deserialization, it was in the serialization -- you must create the serializer with the same base type as the deserializer (I had created the serializer based upon the child type). For what it's worth, working code is below:
{
var child = new Child();
// here is where I went wrong before -- I used typeof(Child), with no known types to serialize
var serializer = new DataContractSerializer(typeof(Parent), new Type[] { typeof(Child) });
var stream = new MemoryStream();
serializer.WriteObject(stream, child);
stream.Position = 0;
serializer = new DataContractSerializer(typeof(Parent), new Type[] { typeof(Child) });
object test = serializer.ReadObject(stream);
stream.Dispose();
Console.WriteLine(test.ToString());
Console.ReadLine();
}

Related

Reading CollectionJson content in Web Api Project

I have a project similar(Almost identical) to Conference API project which is taking similar approach to the noted project for returning CollectionJson content. I am having difficulty Setting the Collection property of the ReadDocument (Line 30) as it does not have any setter. I could bypass this problem by doing the following change
public CollectionJsonContent(Collection collection)
{
var serializerSettings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Newtonsoft.Json.Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
collection.Version = "1.0";
Headers.ContentType = new MediaTypeHeaderValue("application/vnd.collection+json");
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
//var readDocument = new ReadDocument(); {IReadDocument.Collection = collection};
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer,collection);
writer.Flush();
}
_memoryStream.Position = 0;
}
Although above code compiles and to some extent sorts out the problem but then again I will have another problem of not being able to consume the JsonCollection content in my controller unit tests. Consider the following unit test code snippet:
using (var request = CreateRequest())
{
var controller = new TestController(DataService) {Request = request};
var temp = await controller.ListAsync(gridSearchData, sampleSearchData);
if ((temp is NotFoundResult) && (sampleCollection.Any()))
{
Assert.Fail("Controller did not return any result but query did");
}
var json = await temp.ExecuteAsync(cancellationTokenSource);
var readDocument = json.Content.ReadAsAsync<ReadDocument>(new[] {new CollectionJsonFormatter()}, cancellationTokenSource).Result;
}
Since I did not set the collection property of ReadDocument readDocument is always empty and I cant read its content.
How do you asynchronously read the contents of JsonCollection on the client side in WEB API projects?
To get a Clear picture of the approach look at the Conference Web Api
and the authors blog
OK all, this has been fixed. The Collection property is now settable again.
I have just pushed release 0.7.0 with this fix, a major naming refactoring as well as a nice improvement to serialization to not write out empty collections.
Please see the release notes for the changes (especially the naming as the package names and namespaces have changed)
As far as I see from your code, you do not serialize a ReadDocument object, but only a property of it (Collection), and then you try to deserialize that value into a new ReadDocument object.
A sample ReadDocument should serialize like this
"{"Collection": [1,2,3,4,5] }"
But you serialize collection, so you get
"[1,2,3,4,5]"
I recommend a surrogate class for serialization like this
class SerializableReadDocument
{
public Collection Collection { get; set; }
}
and update your serialization code like this
using (var writer = new JsonTextWriter(new StreamWriter(_memoryStream)){CloseOutput = false})
{
var readDocument = new SerializableReadDocument() { Collection = collection };
var serializer = JsonSerializer.Create(serializerSettings);
serializer.Serialize(writer, readDocument);
writer.Flush();
}
But, this will not resolve your problem when you try to deserialize your output since ReadDocument does not have a settable Collection property, deserialization will either fail, or return a ReadDocument object with an empty Collection.
You can use SerializableReadDocument if you like in your unit tests.
I am looking into this and will come up with a solution hopeful this weekend, which will either be to make it a public setter, or make the setter internal and have public ctor that accepts a collection.
Sorry for the difficulty.

Override class name for XmlSerialization

I need to serialize IEnumerable. At the same time I want root node to be "Channels" and second level node - Channel (instead of ChannelConfiguration).
Here is my serializer definition:
_xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>), new XmlRootAttribute("Channels"));
I have overriden root node by providing XmlRootAttribute but I haven't found an option to set Channel instead of ChannelConfiguration as second level node.
I know I can do it by introducing a wrapper for IEnumerable and using XmlArrayItem but I don't want to do it.
Like so:
XmlAttributeOverrides or = new XmlAttributeOverrides();
or.Add(typeof(ChannelConfiguration), new XmlAttributes
{
XmlType = new XmlTypeAttribute("Channel")
});
var xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>), or,
Type.EmptyTypes, new XmlRootAttribute("Channels"), "");
xmlSerializer.Serialize(Console.Out,
new List<ChannelConfiguration> { new ChannelConfiguration { } });
Note you must cache and re-use this serializer instance.
I will also say that I strongly recommend you use the "wrapper class" approach - simpler, no risk of assembly leakage, and IIRC it works on more platforms (pretty sure I've seen an edge-case where the above behaves differently on some implementations - SL or WP7 or something like that).
If you have access to the type ChannelConfiguration, you can also just use:
[XmlType("Channel")]
public class ChannelConfiguration
{...}
var xmlSerializer = new XmlSerializer(typeof(List<ChannelConfiguration>),
new XmlRootAttribute("Channels"));
xmlSerializer.Serialize(Console.Out,
new List<ChannelConfiguration> { new ChannelConfiguration { } });
This should do the trick, if I remember correctly.
[XmlType("Channel")]
public class ChannelConfiguration {
}

Serialize .net linq object to json, exception on datacontext

I'd like to serialize my linq object to json.
The linq object is disconnected, meaning the datacontext was disposed long time ago.
There are "related" objects which were not loaded during the object load process which can't be accessed (When accessed, runtime error return "Cannot access a disposed object." because the datacontext is gone)
Is there any json/xml converter with the ability to serialize this object?
I don't want to chanage the dbml
Is there any serialization object with the ability to configure ignore exception properties or so?
To reproduce that issue, create this object:
public class HelpMeToSerialize
{
public string Name;
public int Age
{
get
{
throw new Exception("Can't access this on runtime");
}
set
{
}
}
}
And simply serialize it with this code or any other code you have:
HelpMeToSerialize obj = new HelpMeToSerialize();
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(obj.GetType());
x.Serialize(Console.Out, obj);
If your Linq generated object has a relationship to another table, which you did not load at the time of creating the object (via Linq2Sql for example), then you can do the following to serialize it:
//assume that your linq created object with class type StronglyTypedLinqObject has a field, ID, and a relationship called RelatedThing
StronglyTypedLinqObject row_from_db = null;
using(var myDatabase = new MyLinqContext(ConnectionString))
{
myDatabase.DeferredLoadingEnabled = false;
//assume this pulls one item back which has a relationship
row_from_db = (from o in myDatabase.TheTableToSelectFrom select o).Single();
row_from_db.RelatedThing = null; //this line may be unnecessary
} // context is disposed now
return Json(row_from_db); //this call should succeed
If you disable deferred loading and try to serialize then it won't try to lazy load the related objects.
Hope that works.
Mustafa
The DataContractSerializer for XML and DataContractJsonSerializer for JSON. You could also try the NewtonSoft JSON library.

Using methods of an object without casting

I have a problem with casting/types and so on.
Firstly, my query is a follow on from another post here:
Initialize generic object from a System.Type
so to continue on from this question, how can I use the methods of my newly created object?
i.e. what I want to do is as follows:
Type iFace = typeof(IService1);
Type genericListType = typeof(System.ServiceModel.ChannelFactory<>).MakeGenericType(iFace);
object factory = Activator.CreateInstance(genericListType, new object[]
{
new BasicHttpBinding(),
new EndpointAddress("http://localhost:1693/Service.svc")
});
var channel = factory.CreateChannel();
by the way, although I am using this application for WCF, this is not a WCF problem
Try using a dynamic object? This allows you to call methods that might or might not exist.
Without dynamic objects:
object factory = Activator.CreateInstance(genericListType, new object[]
{
new BasicHttpBinding(),
new EndpointAddress("http://localhost:1693/Service.svc")
});
Type factoryType = factory.GetType();
MethodInfo methodInfo = factoryType.GetMethod("CreateChannel");
var channel = methodInfo.Invoke(factory) as YourChannelType;

IQueryable<> Serialization - C#-VS2008-WCF

I built my *.dbml file with the required tables and that generated the relationships, 1 to many.
One of my methods in my WCF Service library has this query
IQueryable<Client>localClient = from c in db.Clients
where c.ClientID.Equals(2612)
select c;
foreach(Client currentClient in localClient)
{
//Call serialize method here
}
One of the table which client has a 1 to many relationships is Client - Employee
With 'localClient', I want to serialize that and return to the invoker. However it tells me that the the XML document could not be formed.
This is the InnerException:
A circular reference was detected while serializing an object of type TestDB_Public.Employee.
My serialization code
public string Serialize(object o, XmlSerializerNamespaces ns)
{
try
{
System.IO.MemoryStream m = new System.IO.MemoryStream();
if (ns != null)
serializer.Serialize(m, o, ns);
else
serializer.Serialize(m, o);
m.Position = 0;
byte[] b = new byte[m.Length];
m.Read(b, 0, b.Length);
return System.Text.UTF8Encoding.UTF8.GetString(b);
}
catch (Exception ex)
{
return "Ex = " + ex.ToString();
}
}
Is serialization of IQueryable<> with 1 to many relationships not possible?
What exactly is the error message? Note that you have to serialize something concrete like a list or array of objects (not a query).
If you want queryable over the wire, look at ADO.NET Data Services, which does this.
Also - have you set the serialization mode to "unidirectional" in the dbml designer? As long as there are no loops, it should work fine.
You can't serialise an object graph that has cyclical relationships:
class Employee
{
Employee Manager;
List<Employee> Employees;
}
var bossMan = new Employee();
var emp2 = new Employee{Manager = bossMan}
var bossMan.Employees.Add(emp2);
If you now try to serialise bossman or emp2, you will get the exception.
Have a look at this post, check the Cyclic Object Graphs for a solution.
Marc:
For some reason it is not allowing me to add a comment;
I added this
[Table(Name="dbo.Client")]
[DataContract(IsReference=true)]
public partial class Client: INotifyPropertyChanging, INotifyPropertyChanged
{
..//
private EntitySet<ClEmp> _ClEmp;
[Association(N...)]
[DataMember(Order=70, EmitDefaultValue=false)]
public EntitySet<ClEmp> ClEmps
}
My serialization is this:
DataContractSerializer ser =
new DataContractSerializer(typeof(Client));
var ms = new System.IO.MemoryStream();
ser.WriteObject(ms, r);
ms.Seek(0, System.IO.SeekOrigin.Begin);
var sr = new System.IO.StreamReader(ms);
var xml = sr.ReadToEnd();
when i look at var xml, i do not get my enity set ClEmp.

Categories

Resources