I'm trying to serialize a NameValueCollection over WCF. I keep getting exceptions telling me to add one type after another. After adding them, I finally get
Type 'System.Object[]' cannot be added to list of known types since another type 'System.Collections.ArrayList' with the same data contract name 'http://schemas.microsoft.com/2003/10/Serialization/Arrays:ArrayOfanyType' is already present.
The contract now looks like this:
[KnownType(typeof(NameValueCollection))]
[KnownType(typeof(CaseInsensitiveHashCodeProvider))]
[KnownType(typeof(CaseInsensitiveComparer))]
[KnownType(typeof(string[]))]
[KnownType(typeof(Object[]))]
[KnownType(typeof(ArrayList))]
[DataContract]
public class MyClassDataBase
{
[DataMember]
public NameValueCollection DataCollection = new NameValueCollection();
}
I really dont know what to do to be able to serialize my NameValueColletion.
Another strange thing is that the compiler warns that the CaseInsensitiveHashCodeProvider is deprecated.
The best idea would be to stop using weak types like NameValueCollection and ArrayList. Use Dictionary<string,string> and List<T> instead.
The NameValueCollection doesn't seem exactly to be designed for this use case. There is a series of blog posts on this blog that deals with this problem and presents a possible solution (to wit: use an IDictionary). I haven't tested it though.
Another approach could be to use a wrapper class to adapt NameValueCollection to WCF serialization. Here's a simple example that serializes each name-value pair as a single, comma-delimited string. It then reads that value back into the NameValueCollection upon deserialization:
[CollectionDataContract]
public class NameValueCollectionWrapper : IEnumerable
{
public NameValueCollectionWrapper() : this(new NameValueCollection()) { }
public NameValueCollectionWrapper(NameValueCollection nvc)
{
InnerCollection = nvc;
}
public NameValueCollection InnerCollection { get; private set; }
public void Add(object value)
{
var nvString = value as string;
if (nvString != null)
{
var nv = nvString.Split(',');
InnerCollection.Add(nv[0], nv[1]);
}
}
public IEnumerator GetEnumerator()
{
foreach (string key in InnerCollection)
{
yield return string.Format("{0},{1}", key, InnerCollection[key]);
}
}
}
This would allow you to use the type as a standard [DataMember] property on your DataContracts and you would also use standard WCF serialization techniques.
NameValueCollection does not directly implement the ICollection interface. Instead, NameValueCollection extends NameObjectCollectionBase. This implements the ICollection interface, and the overloaded Add(system.string) method is not implemented in the NameValueCollection class. When you use the XMLSerializer, the XmlSerializer tries to serialize or deserialize the NameValueCollection as a generic ICollection. Therefore, it looks for the default Add(System.String). In the absence of the Add(system.String) method, the exception is thrown.
Use the SoapFormatter class for serialization instead of using XML Serialization. To use the SoapFormatter class, you must add a reference to System.Runtime.Serialization.Formatters.Soap.dll.
// Serializing NameValueCollection object by using SoapFormatter
SoapFormatter sf = new SoapFormatter();
Stream strm1 = File.Open(#"C:\datasoap.xml", FileMode.OpenOrCreate,FileAccess.ReadWrite);
sf.Serialize(strm1,namValColl);
strm1.Close();
// Deserializing the XML file into NameValueCollection object
// by using SoapFormatter
SoapFormatter sf1 = new SoapFormatter();
Stream strm2 = File.Open(#"C:\datasoap.xml", FileMode.Open,FileAccess.Read);
NameValueCollection namValColl1 = (NameValueCollection)sf1.Deserialize(strm2);
strm2.Close();
Related
I have a custom collection that looks like this:
class SpecialReadOnlyCollection<T> : IReadOnlyCollection<T>{
private readonly List<T> entries;
public SpecialReadOnlyCollection(IEnumerable<T> source){
entries = new List<T>(source);
}
...
}
That (among other things) wraps a list but doesn't offer an Add method.
Now I have two other classes:
class A{
public string Name;
public int Value;
}
class ContainerOfA{
public SpecialReadOnlyCollection<A> list;
public ContainerOfA(IEnumerable<A> source){
this.list = new SpecialReadOnlyCollection<A>(source);
}
}
I want to serialize a ContainerOfA. Since I don't like attributes, this is how I build the model and try to serialize.
// Make A serializable
var metaType = Model.Add(typeof(A),true);
metaType.AddField(1,"Name");
metaType.AddField(2,"Value");
metaType.UseConstructor = false;
// Make SpecialCollection serializable
Model.Add(typeof(SpecialReadOnlyCollection<A>),true).AddField(1,"entries");
Model[typeof(SpecialReadOnlyCollection<A>)].IgnoreListHandling = true;
Model[typeof(SpecialReadOnlyCollection<A>)].UseConstructor = false;
// Make container serializable
Model.Add(typeof(ContainerOfA),true).AddField(1,"list");
Model[typeof(ContainerOfA)].UseConstructor = false;
// Initialize the container
A a = new A{Name ="Name", Value =1};
A[] arr = {a};
var container = new ContainerOfA(arr);
// Try and serialize ....
Model.DeepClone(container);
However, when I try to serialize, I get an exception:
Unable to resolve a suitable Add method for SpecialReadOnlyCollection[A]
What I find weird is that if I try to serialize just the list it works fine:
Model.DeepClone(container.list);
Everything works fine as well if I instead of building the model in code use attributes. In fact, everything works if I use attributes only in ContainerOfA and make A and the SpecialReadOnlyCollection serializable via code.
Is there something I am doing wrong? How can I get around this? (Probably the easiest answer is to use attributes ... but I really want to avoid attributes).
tl;dr: How to, via RuntimeTypeModel, use protobuf-net serialize a class that has as a member an IEnumerable that should not be treated as a list (IgnoreListHandling = true).
protobuf-net supports a range of list patterns; in your case, it is trying to use the IEnumerable<T>/GetEnumerator() and Add(T) pattern. It needs both of those things: the IEnumerable<T>/GetEnumerator() is used for serialization, and the Add(T) is used for deserialization. If your custom collection does not have an obvious Add(T) method, then protobuf-net will refuse to work with the collection, because it won't ever be able to deserialize. That is what the method is telling you.
The only way to get around this is probably with a mutable DTO model, rather than this read-only collection. There are things like implicit type conversions that protobuf-net supports, but not for this scenario.
I'm doing some work for a JIRA REST lib. It uses JSON for communication.
Part of the objects I receive in JSON format are know. The others can be of several different formats. So what I have done is created an object with properties that hide a dictionary
public IssueFields Fields { get; set; }
public IssueType IssueType
{
get { return Fields.IssueType; }
set { Fields.IssueType= value; }
}
IssueFields
{
private Dictionary<string, Field> _fields = new Dictionary<string, Field>();
public string IssueType
{
get { return GetFieldByName<IssueType>(IssueTypeFieldName); }
set { SetFieldByName(IssueTypeFieldName, value); }
}
public T GetFieldByName<T>(string fieldName) where T : class
{
return _fields.ContainsKey(fieldName) ? _fields[fieldName] as T: null;
}
public void SetFieldByName(string fieldName, Field field)
{
if (_fields.ContainsKey(fieldName))
{
_fields[fieldName] = field;
}
else
{
_fields.Add(fieldName, field);
}
}
So I have a bunch of classes like that. I can deserialize into them no problem since JavaScriptSerializer (or any other JSON deserializer) just takes the values and puts them into properties of the objects objects automatically. However there are a bunch of unknown fields all starting with "customField_XXXXX".
What I am currently doing is overriding the JavaScriptSerializer and manually putting EVERYTHING into place. Another idea I got from someone else's code was to re-serialize the dictionary inside the JavaScriptConverter override, then deserialize it into the issue, then put everything else in manually from the dictionary, but that adds a lot of overhead and certainly will raise more than a few eyebrows.
public class MyConverter : JavaScriptConverter
{
private static JavaScriptSerializer _javaScriptSerializer;
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
string json = _javaScriptSerializer.Serialize(dictionary);
Issue issue = _javaScriptSerializer.Deserialize<Issue>(json);
// Then add the rest of my objects manually
Is there any way to get the object back with whatever it could serialize AND the dictionary so I can fill in anything it couldn't on my own? I just haven't been able to find anything other than this method....
Thanks!
Json.Net has a built-in "Extension Data" feature that might solve your problem. After deserializing known properties into your class, it automatically handles adding any "extra" object properties to a dictionary. You can read about it on the author's blog. Also check out this answer for some example code.
I am attempting to save/load a class to an xml file that contains generic types using a DataContractSerializer. I have the save working, but have realized I can't load it because I don't have the list of knownTypes for the deserializer.
Is there a way of serializing/deserializing this class that would allow me to deserialize it without referencing any of the stored types directly?
Here is my SessionVariables class that I am trying to save/load:
[DataContract]
public class SessionVariables
{
[DataMember]
private Dictionary<Type, ISessionVariables> _sessionVariables = new Dictionary<Type, ISessionVariables>();
private object _syncLock = new object();
public T Get<T>()
where T : ISessionVariables, new()
{
lock (_syncLock)
{
ISessionVariables vars = null;
if (_sessionVariables.TryGetValue(typeof(T), out vars))
return (T)vars;
vars = new T();
_sessionVariables.Add(typeof(T), vars);
return (T)vars;
}
}
public IList<Type> GetKnownTypes()
{
IList<Type> knownTypes = new List<Type>();
knownTypes.Add(this.GetType().GetType()); // adds System.RuntimeType
foreach (Type t in _sessionVariables.Keys)
{
if (!knownTypes.Contains(t))
knownTypes.Add(t);
}
return knownTypes;
}
}
The different modules of the application extend the ISessionVariables interface to create their own set of session variables, like this:
[DataContract]
public class ModuleASessionVariables : ISessionVariables
{
[DataMember]
public string ModuleA_Property1{ get; set; }
[DataMember]
public string ModuleA_Property2 { get; set; }
}
[DataContract]
public class ModuleBSessionVariables : ISessionVariables
{
[DataMember]
public string ModuleB_Property1{ get; set; }
[DataMember]
public string ModuleB_Property2 { get; set; }
}
And a singleton instance of the SessionVariables class is used to access session variables, like this:
singletonSessionVariables.Get<ModuleASessionVariables>().ModuleA_Property1
singletonSessionVariables.Get<ModuleBSessionVariables>().ModuleB_Property2
I got the save working like this:
using (FileStream writer = new FileStream(#"C:\test.txt", FileMode.Create))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(SessionVariables), singletonSessionVariables.GetKnownTypes());
dcs.WriteObject(writer, singletonSessionVariables);
writer.Close();
}
However this method does not work to deserialize the class because I don't know it's known types.
Can I serialize and deserialize generic types when I don't have direct library references to any of the types used? And if so, how?
The problem here is that you aren't just wanting to serialize data, but you also want to serialize data about your data, i.e., (cue the dramatic chipmunk) metadata.
That metadata, in this case, are the types of the models that held the data originally. Normally, this isn't an issue, but as you've discovered if you're taking advantage of polymorphism in your design, your single collection may contain two or more different types, each of which needs to be deserialized to their original type.
This is usually accomplished by saving this Type metadata to the serialized result. Different serialization methods does this in different ways. Xaml serialization uses xml namespaces associated with .net namespaces, then names the elements after the original type name. Json.net accomplishes this via a specific named value saved to the json object.
The default DataContractSerializer is not Type aware. Therefore you need to replace it with a version that understands the .NET Type system and can serialize/deserialize Type metadata to the resulting xml. Luckily, one already exists in the framework, the NetDataContractSerializer.
And that's how you pad a link-only answer. The Aristocrats.
You could accomplish this using a custom DataContractResolver. This allows you to plug into the deserialization pipeline and provide a type to deserialize into based upon the type/namespace that is found in the serialized graph.
Here's a good article on it:
http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspx
IDesign has an implementation of a resolver that can be used for dynamic discovery of types on their site: http://idesign.net/Downloads/GetDownload/1848 (you will probably have to make some modifications to handle generics)
I have a curious case of this serialization issue - which has been asked many times on this site and I have gone through a few of these questions and tried the usual items to no avail:
Add [XmlInclude] to the class throwing the error
Remove namespaces
Add a different namespace to each class
To explain further, I have provided a simplified version of my code below. Essentially I am using a WebServiceHost object to run a RESTful service and one of my endpoints returns an object serialized as XML (I have annotated the object with [DataContract] and [DataMember] attributes). This object contains a SerializableDictionary<string, object> (here) where the value has been typed as object. I believe this is why it is failing:
Works fine when the value is assigned a primitive
When I assign a custom object to the KV pair V, I get the unexpected type exception probably because the Serializer does not know how to serilaize the object / some sort of namespacing issue
Obviously, I am unable to annotate Object.cs with [XmlInclude] and because it is a service and I am not myself serializing I cannot using something like
new Serializer(typeof(...), new Type[] { ... }}
Any idea's of what I can do? I thought about not typing the dict value as object and rtaher comething more concrete but the problem is that this value can take primitives or cusotm types. Some code to explain the above:
Edit: Updated the code below to make it more clear
[DataContract]
public class ResponseObject
{
[DataMember(Name = "data")]
public SerializableDictionary<string, object> Data { get;set; }
public ResponseObject()
{
Data = new SerializableDictionary<string, object>();
}
}
...
var d1 = new ResponseObject();
d1.Data.Add("some key", "some value"); //WORKS AND SERIALIZES PERFECLTY
var d2 = new ResponseObject();
d2.Data.Add("some other key", new SomeOtherObjecT());
var d3 = new ResponseObject();
d3.Data.Add("another key", d2); //THIS THROWS THE UNEXPECTED TYPE ERROR WHEN SEIRLAIZING SomeOtherObject
Edit: The error is thrown in SerializableDictionary where it is attempting to serialize an object of type ResponseObject. The two are in seperate projects - if that is significant?
Normally, you should add an [XmlInclude] to the ResponseObject class. In this case, it doesn't work because of the SerializableDictionary that you're using. That class creates another XmlSerializer in its implementation, and therefore it doesn't care about your [XmlInclude]'s. Basically it just cannot handle your use case. You should switch from the XmlSerializer to the DataContractSerializer which handles the Dictionary class and supports the [KnownType] attribute to register additional types: http://pastebin.com/vGLSaxHF . Also note that it's pointless to add [DataContract] and [DataMember] attributes in your current case because the XmlSerializer ignores those attributes, they are used by the DataContractSerializer only. Or if you're not sure how to change your serializer (I know I'm not) then you should either not be using a Dictionary or change the SerializableDictionary implementation to handle the dynamic object types that you want to use (find every line where it creates a new XmlSerializer). Or, as an alternative, define a base class for all your objects that you will ever put into the dictionary and do it like this:
[XmlInclude(typeof(Class1), XmlInclude(typeof(Class2)), etc]
public class AbstractBase { }
public class Class1 : AbstractBase { ... }
public class Class2 : AbstractBase { ... }
public class BigClass {
public SerializableDictionary<string, AbstractBase> Dictionary { get; set; }
}
This way, when the SerializableDictionary creates its own XmlSerializer, it will recognize the AbstractBase and from there, all of its descendants.
I'm sure this question has been asked over and over again, but for some reason, I still can't manage to get this to work.
I want to deserialize a JSON object that contains a single member; a string array:
[{"idTercero":"cod_Tercero"}]
This is the class that I'm trying to deserialize into:
[DataContract]
public class rptaOk
{
[DataMember]
public string idTercero { get; set; }
public rptaOk() { }
public rptaOk(string idTercero)
{
this.idTercero = idTercero;
}
}
This is the method that I try to deserialize:
public T Deserialise<T>(string json)
{
DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T));
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
T result = (T)deserializer.ReadObject(stream);
return result;
}
}
And so I try to fill the object:
rptaOk deserializedRpta = deserializarOk(rpta);
But for some reason, this returns ""
MessageBox.Show(deserializedRpta.idTercero);
Without any dependencies outside of the .net framework, you could do it this way
[DataContract(Name="rptaOk")]
public class RptaOk
{
[DataMember(Name="idTercero")]
public string IdTercero { get; set; }
}
[CollectionDataContract(Name="rptaOkList")]
public class RptaOkList : List<RptaOk>{}
var stream = new StreamReader(yourJsonObjectInStreamFormat);
var serializer = new DataContractSerializer(typeof(RptaOkList));
var result = (RptOkList) serializer.ReadObject(stream);
I don't know if your're wiling to change the library that you're using, but I use library "Newtonsoft.Json" to desserialize JSON objects, it's pretty easy to use
[HttpPost]
public void AddSell(string sellList)
{
var sellList = JsonConvert.DeserializeObject<List<Sell>>(sellListJson);
BD.SaveSellList(sellList);
}
As you can see you can deserialize a whole json object list to a List<> fo the type "Sell", an object that i've created... And, of course, you can do that to an array too. I don't know the correct syntax for this, but you can convert this list to an array afterwards
Hope this helps
I think you're making this a lot more difficult than it needs to be. Firstly, your sample json and the class you're trying to deserialize into do not have an array of strings. They have a single property of type string. Secondly, why are you using this class DataContractJsonSerializer? You're not doing anything with it that you can't get from a simple call to json.NET's generic deserialization method. I would remove all of your code except the class definition and replace it with this simple one liner;
rptaOk[] myInstances = JsonConvert.DeserializeObject<rptaOk>(jsonString);
Also, no matter what the structure of your json is, if you have a class to correctly model it that method will correctly deserialize it. If you want to enforce some kind of contract I recommend using json schemas which json.NET also supports. If you use a schema it enforces a rigid contract, if you attempt to deserialize into an object there is something of an implicit contract. I don't know every scenario which will cause it to throw, but if your json is too far from the class definition it will. If your class has properties that don't appear in the json I believe they will just get initialized with the default values for that type.
EDIT: I just noticed your json is actually an array of objects. So you simply need to make the lhs of that assignment an array or rptaOk objects, rather than a singleton.