I'm developing a desktop application with .NET. I'd like to save some data into a file in a way that would later give me some degree of freedom in changing the data I'm saving, such as adding new fields, while retaining the possibility to read saves from older formats.
This answer recommends to use DataContractSerializer instead of BinaryFormatter.
However I can't use the [DataContract] attribute on my classes in the project. After using System.Runtime.Serialization; I still get errors about unknown types.
The project targets .NET Framework 4.
I've learned that Data Contracts are part of the WCF framework, I assume I should somehow configure my project to use it. How?
In C# namespaces can be shared across multiple assemblies. You have to add a reference to System.Runtime.Serialization.dll, which contains [DataContract] attribute.
probably you are missing to specify the Know Type attribute when it is needed
Have a look at the below example:
public interface ICustomerInfo
{
string ReturnCustomerName();
}
[DataContract(Name = "Customer")]
public class CustomerTypeA : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract(Name = "Customer")]
public class CustomerTypeB : ICustomerInfo
{
public string ReturnCustomerName()
{
return "no name";
}
}
[DataContract]
[KnownType(typeof(CustomerTypeB))]
public class PurchaseOrder
{
[DataMember]
ICustomerInfo buyer;
[DataMember]
int amount;
}
you have to specify the type of ICustomerInfo otherwise the serialization engine cannot guess the type
Just add wcf service template to your application and declare your function and data members their and reference wcf in your project.
Related
I have some problems while using Entity Framework.
I always use a JSONHelper class to convert a list of string to JSON.
Here is the JSONHelper Class:
public static class JSONHelper
{
public static string ToJSON(this object obj)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj);
}
public static string ToJSON(this object obj, int recursionDepth)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RecursionLimit = recursionDepth;
return serializer.Serialize(obj);
}
}
This class creates JSON. But sometimes it gives me an error saying that "A circular reference was detected in Entity..........."
So in some of the properties of the classes that entity-framework created for me, I have to use [ScriptIgnore] Attribute. Then it works fine.
Now, let's say I made a small change in database. So I will refresh my ADO.Net Entity Data Model. Then I again have to open all those class files and write [ScriptIgnore].
So, I decided to make a change in template designer. But I could not find the files that entity framework uses to create my POCO classes.
Any Ideas????
Please don't tell me to change the way of converting List to JSON.
I recommend that you use the NewtonSoft.Json serializer. It works very well and has settings options for handling many things, including circular references. There is a lot of good documentation and help available for that serializer. Don't reinvent the wheel.
Circular references are controlled like this:
SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
I believe this answer describes what you need to do.
For any entity which you need to apply the ScriptIgnore attribute, you can make a partial class which does the attribute decoration.
Say your entity is like this
public partial class Entity
{
public int Id { get; set; }
public string Name { get; set; }
}
You can create a separate partial class like this
public interface IEntity
{
[ScriptIgnore]
string Name { get; set; }
}
public partial class Entity:IEntity
{
}
You can achieve this by modifying the T4 template file (.tt file). Take a look at the .tt file in a text editor and you will be able to see where your properties are being created. Insert the [ScriptIgnore] attribute in your template and it will be automatically included when you re-generate your entities.
You might need to include an appropriate 'using' at the top of the template etc.
You can do a lot by editing the T4 template, we used it to automatically have our entities implement INotifyPropertyChanged for all properties.
I'm writing a C# test automation to validate web services that return JSON strings. I created a DataContract that mapped to what was being returned. Assume this is what was being returned:
{"DataModule" : {"required":"false", "location":"toolbar"}}
My test automation was working fine, but then I started getting this error:
"The data contract type 'DataModule' cannot be deserialized because
the required data members 'required, location' were not found."
I checked the JSON and the data module was now
{"DataModule" : {"width":"400", "height":"320"}}
I discovered that the dev implementation is that if the first type of data module is encountered, the client parses that and creates a button on a toolbar. If the second type of data module is returned, the button appears on the toolbar AND a panel appears in another location with those measurements.
Is there a way to either create optional members in a data contract OR implement conditional deserialization to account for JSON objects that may have multiple implementations?
If you declare the model with all of the likely properties, only the ones found in the JSON string will be populated:
public class DataModule
{
public bool Required { get; set; }
public string Location { get; set; }
public string Width { get; set; }
public string Height { get; set; }
}
#dave-s
I had already tried adding all of the properties, but since I knew I was on the right track, your suggestion keyed me into something else. The properties were all decorated with
[System.Runtime.Serialization.DataMemberAttribute(IsRequired = false)]
But the class itself, was decorated with only [Serializable]. When I changed [Serializable] to
[System.Runtime.Serialization.DataContractAttribute()]
it started working.
I'm having an strange error when trying to save an object into isolated storage. I have a class that has some properties, here's the code :
[DataContract]
public class ExerciseStatistic
{
[XmlIgnore]
public int CorrectAnswers
{
get
{
return Attempts.Where(a => a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int IncorrectAnswers
{
get
{
return Attempts.Where(a => !a.AttemptAnswerIsCorrect).Count();
}
}
[XmlIgnore]
public int AnswerAttempts
{
get { return Attempts.Count; }
}
public List<AnswerAttempt> Attempts { get; set; }
public ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>();
}
}
public class AnswerAttempt
{
public DateTime AttemptDate { get; set; }
public string AttemptTargetName { get; set; }
public string AttemptName { get; set; }
public bool AttemptAnswerIsCorrect { get; set; }
}
However, when trying to save it with this sentence :
IsolatedStorageSettings.ApplicationSettings["a"] = new ExerciseStatistic()
{
Attempts = new List<AnswerAttempt>()
{
new AnswerAttempt()
{
AttemptAnswerIsCorrect = true,
AttemptDate = DateTime.Now,
AttemptName = "lala",
AttemptTargetName = "lala2"
},
new AnswerAttempt()
{
AttemptAnswerIsCorrect = false,
AttemptDate = DateTime.Now,
AttemptName = "lalab",
AttemptTargetName = "lalab2"
}
}
};
I'm getting an exception like this one (i changed a bit the signature of the code with fake names, but for the example it serves its purpose) :
Type 'XX.Model.FirstClass.SecondClass' cannot be serialized. Consider
marking it with the DataContractAttribute attribute, and marking all
of its members you want serialized with the DataMemberAttribute
attribute.
I don't understand why the serializer is trying to serialize an object of my model (which is not serializable) when the class that I'm giving it doesn't have any references to that kind of type... what am i missing? -> nope, i don't want to add datacontract attributes to classes that i don't need and am not planning to serialize, so please don't answer with this :)
You might experience this problem if you work through the reference procedure in "Walkthrough: Consuming OData with MVVM for Windows Phone" at http://msdn.microsoft.com/en-us/library/hh394007(v=VS.92).aspx
When you get to the point where you call :
Return DataServiceState.Serialize(_context, collections);
You might get an InvalidDataContractException with the message:
Type 'DataBoundApp1.Northwind.NorthwindEntities' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute.
Thanks to the answer by Daniel Perez, I was able to resolve this problem and I am documenting my steps to clarify the solution for others:
Show hidden files in Solution Explorer
Open the file "Reference.cs" (under your Service Reference, expand
Reference.datasvcmap)
If your Data Service Context class is missing the [DataContract]
attribute, add it as shown here:
.
namespace OCC.WindowsPhone.OrlandoCodeCampService
{
[DataContract] <--- I ADDED THIS
public partial class OrlandoCodeCampEntities : global::System.Data.Services.Client.DataServiceContext
{..}
Once I added the DataContract attribute, the problem went away!
It seems to me you try to exclude properties from serialization by using XmlIgnore.
From the documentation:
You can opt out members from serialization by using the IgnoreDataMemberAttribute.
so try using IgnoreDataMemberAttribute instead of XmlIgnore to opt out members from serialization.
I also had some troubles with DataContract in the very same situation as you, therefore I reverted to plain old XML serialization to strings, which i then stored in isolated storage. This also eases debugging.
I don't think that this is a proper answer, but it's what i had to do in order to fix it.
After changing some more the code, i realised that this was failing EVEN if I wasn't saving anything to the isolated storage. Just declaring a DataContract attribute on the type made the error arise. I must think that WP7's framework at some point is parsing all classes that have this attribute, and for some strange and obscure reason (which i can't find) it's looking for them in other classes as well. I added the DataContract attributes in the classes that the framework is complaning about, and also some KnownType attributes as well, and everything started to run smoothly... weird weird... if someone can shed some light into this, i'd be happy (i hate it when i solve a problem but without knowing the exact cause)
I was trying to implement parsing a JSON response as shown here for my Windows Phone 7 project in C#. But I am stuck with a compilation error as "The type or namespace name 'Serializable' could not be found (are you missing a using directive or an assembly reference?)"
I have the imports using System.Runtime.Serialization;
using System.Runtime.Serialization.Json; I am not sure what are import I am missing. I tried to include using System.ServiceModel.Web; But the Web part is not recognized.
I thought my project is not pointing to the right framework from here. But in the Assembly information, there is no option for me to change the target framework.
This looks like a similar problem to mine, but I couldn't find the JSON.NET in .net dlls which is filtered for Windows Phone.
Can someone help me to get this JSON thing working for Windows Phone 7.
Thank in Advance.
EDIT - 7/3/11
My Jason response is
{ "serviceresponse" : { "servicename" : "RequestRegisterUser", .....
And my Response objects are:
[DataContract]
public class serviceresponse
{
[DataMember]
public String servicename { get; set; }
.
.
.
And my Deserialize method:
public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
obj = (T)serializer.ReadObject(stream);
return obj;
}
}
Now I am getting this error after Deserializing the response:
servicename Could not evaluate expression string
( I could not import System.ServiceModel.Web though I have the dll in the reference. A compilation error on the .Web part (The type or namespace name 'Web' does not exist in the namespace 'System.ServiceModel') )
EDIT After more research, I found my response when viewed in the debugger is actually
{
\"serviceresponse\": {
\"servicename\": \"RequestRegisterUser\",.....
I searched for this and found this could be a problem. How can I format it to correct JSON String??
You need to add a reference to both System.Runtime.Serialization and System.ServiceModel.Web assemblies. The DataContractJsonSerializer is defined in System.ServiceModel.Web assembly in the Silverlight version of the framework, that's why you need the extra assembly reference.
And by the way JSON.NET is a a popular open-source JSON framework for .Net and you could find more about it here. It's not part of the .Net framework, that's why you can't find it.
Edit:
About the compilation, in Silverlight the DataContractJsonSerializer is in the System.Runtime.Serialization.Json namespace, but in the assembly System.ServiceModel.Web (in System.ServiceModel.Web.dll), which is a bit confusing. So you use it like this - System.Runtime.Serialization.Json.DataContractJsonSerializer, but need the extra assembly reference. You also need to reference the System.Runtime.Serialization assembly as well, because that is where the DataContract attribute is defined. I see you have already successfully compiled the code, but I hope the extra explanation makes it more clear for future readers.
About the serialization itself - as you have already found out, you will need two objects, simply because that's the structure of the json. However, the DataContract and DataMember attributes have a Name property that you can use instead of changing the name of the fields. Also, you can use properties instead of fields if you like.
For example:
[DataContract]
public class ServiceResponse
{
[DataMember(Name = "servicename")]
public string ServiceName { get; set; }
}
[DataContract]
class Response
{
[DataMember(Name = "serviceresponse")]
public ServiceResponse ServiceResponse { get; set; }
}
And one last thing - you don't need the call to Activator.CreateInstance(); in your Deserialise method.
It certainly would help if you posted your code. So I can only guess:
I assume you have something like this:
[Serializable]
public class Response
{
[DataMember]
public string name { get; set; }
...
}
But that's a mix-up of two serialization concepts, one of which is not supported in Phone 7. The correct attributes are DataContract and DataMember:
[DataContract]
public class Response
{
[DataMember]
public string name { get; set; }
...
}
I found the issue. Though my class name is "serviceresponse", I used another wrapper class as
public class Response
{
public serviceresponse serviceres;//Don't Do this....
}
where I used the variable name for serviceresponse as serviceres. But when I changed it to " serviceresponse" its all working.
public class Response
{
public serviceresponse serviceresponse;//This fixed it.
}
So I am working with WCF and my services return types that contain generic lists. WCF is currently converting these to arrays over the wire. Is there a way I configure WCF to convert them back to lists afterwards? I know there is a way by clicking advanced when you add a service reference but I am looking for a solution in configuration files or something similar.
[DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public ICollection<AnotherObject> Objects { get; set; }
}
It is very odd, also, because one service returns it as a list and the other as an array and I am pretty sure they are configured identically.
At the advanced tab when adding your service reference you can set this option as well. standard Arrays are set.
I think this is dent with purely from the way that the client tool generates the contracts from the WSDL. In my case, I made a reusable .dll that contains my [OperationContract] and [DataContract] classes, and use it from both the client and the server, instead of generating one with SvcUtil. This way I preserve my lists of generics.
In addition, take care not to have both arrays and generics in the classes from which you serialize the instances with WCF, because you'll get a problem during deserialization : everything will be converted either to ArrayOf (if you don't change the configuration) or to Collection Type.
As result you will get errors during deserialization from the WCF code trying to assign an array where you wait a Collection and conversely.
This was just my 2cent advice from what I learned during a small project with WCF. :)
I found a solution that was much simpler and worked well enough for me, although it might not work for others. I simply switched from using ICollection (IList also produces this result) to List. It worked fine after that.
Solution from Here.
I also found a possible configuration solution from Here near the bottom.
<CollectionMappings>
<CollectionMapping TypeName="ChangeTracker.ChangeTrackingCollection'1" Category="List" />
</CollectionMappings>
Instead of use ICollection<AnotherObject> in your data contract, that will be generated in client application as a AnotherObject[].
Try this:
define a new data contract
[CollectionDataContract]
public class AnotherObjectCollection : List<AnotherObject> {}
in your code:
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
in Visual Studio (same to svcUtil), the client proxy code will appear like this:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]
[System.Runtime.Serialization.CollectionDataContractAttribute(Name="AnotherObjectCollection", Namespace="http://schemas.datacontract.org/2004/07/SampleObject", ItemName="AnotherObject")]
[System.SerializableAttribute()]
public class AnotherObjectCollection : System.Collections.Generic.List<AnotherObject> {}
DataContract(IsReference = true)]
public class SampleObject
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public AnotherObjectCollection Objects { get; set; }
}
This also works for built-in .NET types.
antonio