I have an object I would like to serialize into json in Unity to send to a service via REST call. In .NET I know you can easily ignore null properties.
[JsonProperty("some_model", NullValueHandling = NullValueHandling.Ignore)]
public class SomeModel
{
....
}
Is this possible using FullSerializer in Unity?
Currently I have
fsData data = null;
fsResult r = sm_Serializer.TrySerialize(objectToSerialize, out data);
string sendjson = data.ToString();
Is there a similar attribute I can add to the DataModel using FullSerializer?
[fsObject(ignoreNullProperties)]
public class SomeModel
{
....
}
Looks like one answer is custom converters.
private static fsSerializer sm_Serializer = new fsSerializer();
[fsObject(Converter = typeof(CustomConverter))]
public class SomeClass
{
string MyProp { get; set; }
}
public class CustomConverter : fsConverter
{
private static fsSerializer sm_Serializer = new fsSerializer();
public override bool CanProcess(Type type)
{
return type == typeof(SomeClass);
}
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
{
throw new NotImplementedException();
}
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
{
SomeClass someClass = (SomeClass)instance;
serialized = null;
Dictionary<string, fsData> serialization = new Dictionary<string, fsData>();
fsData tempData = null;
if (someClass.MyProp != null)
{
sm_Serializer.TrySerialize(someClass.MyProp, out tempData);
serialization.Add("myProp", tempData);
}
serialized = new fsData(serialization);
return fsResult.Success;
}
}
This works but any other suggestions are greatly appreciated!
Related
How can I create deserialization method that can take an object of class or any of derived classes?
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
I want to get something like serialization method that defines type of input object:
public string serialize(object obj)
{
XmlSerializer serializer = new XmlSerializer(obj.GetType());
StringWriter serialized = new StringWriter();
serializer.Serialize(serialized, obj);
return serialized.ToString();
}
But when I read an XML from DB I can't define the type of object, so I can't pass it to XmlSerializer. It may be the Config object or any of derived classes
Please help. How can I define the type of input object?
[XmlInclude(typeof(CustomConfig1))]
[XmlInclude(typeof(CustomConfig2))]
public class Config
{
public string appname;
}
Then just serialize/deserialize specifying typeof(Config); the library will give you back an instance of the appropriate type based on the data.
Edit: full example, including the preference to not hard-code the sub-types:
using System;
using System.IO;
using System.Xml.Serialization;
public class Config
{
public string appname;
}
public class CustomConfig1 : Config
{
public string CustomConfig1Param1;
public string CustomConfig1Param2;
}
public class CustomConfig2 : Config
{
public string CustomConfig2Param1;
public string CustomConfig2Param2;
}
static class Program
{
static void Main()
{
var original = new CustomConfig1
{
appname = "foo",
CustomConfig1Param1 = "x",
CustomConfig1Param2 = "y"
};
var xml = Serialize(original);
var clone = DeserializeConfig(xml);
Console.WriteLine(clone.appname);
var typed = (CustomConfig1)clone;
Console.WriteLine(typed.CustomConfig1Param1);
Console.WriteLine(typed.CustomConfig1Param2);
}
public static string Serialize(Config obj)
{
using (var serialized = new StringWriter())
{
GetConfigSerializer().Serialize(serialized, obj);
return serialized.ToString();
}
}
public static Config DeserializeConfig(string xml)
{
using(var reader = new StringReader(xml))
{
return (Config)GetConfigSerializer().Deserialize(reader);
}
}
static Type[] GetKnownTypes()
{
// TODO: resolve types properly
return new[] { typeof(CustomConfig1), typeof(CustomConfig2) };
}
private static XmlSerializer configSerializer;
public static XmlSerializer GetConfigSerializer()
{
return configSerializer ?? (configSerializer =
new XmlSerializer(typeof(Config), GetKnownTypes()));
}
}
this might have a easy/quick solution, but from my investigation I didn't manage to find it.
public interface IBaseMessage
{
}
public interface IRefreshMessage : IBaseMessage
{
}
Both are implemented by concrete classes in a Library.Message
They are stored inside a List<IBaseMessage> and when I serialize the object to be send over the network (one by one) it is marked as IBaseMessage even tough some are IRefrehMessage
Issue: on deserialization (on another PC) they are reconstructed as IBaseMessage and all the information from IRefreshMessage is lost.
Assumption: I believe there must be some JSON class attribute that allows me to specify into what to be serialized?
Thank you, and sorry for a rather silly question
Edit:
Using this class to serialize/deserialize:
using Newtonsoft.Json.Linq;
public class JMessage
{
public Type Type { get; set; }
public JToken Value { get; set; }
public static JMessage FromValue<T>(T value)
{
return new JMessage { Type = typeof(T), Value = JToken.FromObject(value) };
}
public static string Serialize(JMessage message)
{
return JToken.FromObject(message).ToString();
}
public static JMessage Deserialize(string data)
{
return JToken.Parse(data).ToObject<JMessage>();
}
Once is calls
string data = JMessage.Serialize(JMessage.FromValue(message));
I get:
{
"Type": "Universal_Tool_Manager.Messages.Message.Interfaces.IBaseMessage, Universal Tool Manager.Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
"Value": {
"Propagation": 2,
"State": 0
}
}
Hope this helps.
JSON.NET allows you to specify how to handle serialization and deserialization using JsonSerializerSettings:
class Program
{
static void Main(string[] args)
{
var items = new List<Test>
{
new Test() {Hello = "Hi"},
new TestDerived() {Hello = "hello", Second = "World"}
};
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.Objects
};
var text = JsonConvert.SerializeObject(items, settings);
var data = JsonConvert.DeserializeObject<IEnumerable<Test>>(text, settings);
}
}
public class Test
{
public string Hello { get; set; }
}
public class TestDerived : Test
{
public string Second { get; set; }
}
In this sample the generated JSON will contain a name of the actual type which was serialized and JSON.NET will use this information to deserialize back to the correct derived type - data[1] is TestDerived in my example code.
To use JsonSerializerSettings with FromObject and Parse methods, you will need to create an instance of JsonSerializer:
var serializer = sonSerializer.Create(settings);
JToken.FromObject(o, serializer)
//and
JToken.Parse(data).ToObject<JMessage>(serializer);
I've asked the wrong question here :)
public static JMessage FromValue<T>(T value)
{
return new JMessage { Type = **typeof(T)**, Value = JToken.FromObject(value) };
}
The issue was on typeof(T) which of course it will always return the base interface IBaseInterface since i'm using a
List<IBaseInterface>,
The solution was to use value.GetType(),
sorry for the rookie mistake
I have this class:
[Serializable]
public class ServerResponse
{
public string[] Response { get; set; }
public object Packet { get; set; }
public ServerResponse(string[] response, object packet){
this.Response = response;
this.Packet = packet;
}
public string[] getResponse() { return this.Response; }
public object getPacket() { return this.Packet; }
}
And use the following to convert to JSON:
static void writeToClient(TcpClient client, object message)
{
string json = JsonConvert.SerializeObject(message);
NetworkStream clientStream = client.GetStream();
byte[] buffer = NetworkHelper.GetMessageAsBytes(json);
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
However, the Packet is never serialized. Can anyone see what I'm doing wrong? Sample output:
{"Response":["message","Authenticated"],"Packet":{}}
And this is the actual class I'm attempting to serialize:
[Serializable]
public class ActiveGameSession
{
private LoginServerSettings serverSettings;
private List<UserCharacter> sessionCharacters = new List<UserCharacter>();
private UserAccount sessionAccount;
public ActiveGameSession()
{
this.sessionCharacters = new List<UserCharacter>();
this.sessionAccount = null;
this.serverSettings = null;
}
public ActiveGameSession(List<UserCharacter> chars, UserAccount session)
{
this.sessionCharacters = chars;
this.sessionAccount = session;
}
#region setters
public void setCharacters(List<UserCharacter> chars)
{
this.sessionCharacters = chars;
}
public void setSession(UserAccount session)
{
this.sessionAccount = session;
}
public void setServerSettings(LoginServerSettings settings)
{
this.serverSettings = settings;
}
#endregion
#region getters
public List<UserCharacter> getCharacters()
{
return this.sessionCharacters;
}
public UserAccount getAccount()
{
return this.sessionAccount;
}
public LoginServerSettings getSettings()
{
return this.serverSettings;
}
#endregion
}
JSON.NET may not support serialization of an object of type Object. Try using a more concrete type. If you want to have multiple packet types you can try using generics or derive from a common base class.
Edit:
Also there are no public properties of ActiveGameSession to be deserialized, please note that the getXXX, setXXX methods is not recognized as properties.
You need to cast the object representing the Packet to a type that JSON.NET can serialize. Try casting to a string and try again.
I Have a Interface that Implemented By many Class, In My Interface i have a Class that fill by child class that Implemented my Interface.
my work is , i find the class that Implement My Interface and call the class that filled, then i Serialize that classes by NewSoft.Json and write to file then in another place i deserialize that file and create instance of my root class and pass it to the classes that want to read and use it.
my problem is when i deserialize and fill in my proprties, i cant use my Data Proprties because json cant give me the right thing.
for Example in my test Scenario i have a class that fill my class proprties and file DataTable in Data Proprties then i Serialize it and when i deserialize ir and want to convert it to datatable again have some error
i Get type of Data Proprties and say is Newtonsoft.Json.Linq.JArray
and my other question is how to write the common code that can convert all of the type the classes fill it befor
My Code is Blow :
my Interface :
public interface ISyncable
{
List<SyncablePackage> GetSyncableEntityDetails();
void SetSyncablePackage(SyncablePackage syncablepackage);
List<Guid> DependentSoftwares { get; set; }
}
My Class :
[Serializable]
[DataContract]
public class SyncablePackage : ISerializable,IDisposable
{
public SyncablePackage() { }
public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
{
//Get the values from info and assign them to the appropriate properties
EntityGuid = (Guid)info.GetValue("EntityGuid", typeof(Guid));
Name = (string)info.GetValue("Name", typeof(string));
Data = (object)info.GetValue("Data", typeof(object));
DataFileID = (List<int>)info.GetValue("DataFileID", typeof(List<int>));
}
//Serialization function.
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("EntityGuid", EntityGuid);
info.AddValue("Name", Name);
info.AddValue("Data", Data);
info.AddValue("DataFileID", DataFileID);
}
[DataMember]
public Guid EntityGuid;
[DataMember]
public string Name;
[DataMember]
public object Data;
[DataMember]
public List<int> DataFileID;
[DataMember]
private IntPtr handle = new IntPtr();
[DataMember]
private Component component = new Component();
[DataMember]
private bool disposed = false;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
Name = null;
Data = null;
DataFileID = null;
component.Dispose();
}
CloseHandle(handle);
handle = IntPtr.Zero;
disposed = true;
}
}
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
~SyncablePackage()
{
Dispose(false);
}
}
the sample class fill data
then i get it and serialize it like blow :
#region Handle Null Value
if (package.DataFileID == null)
package.DataFileID = new List<int>() { int.MinValue };
if (package.Data == null)
package.Data = new DataTable();
if (package.Name == null)
package.Name = "NoName";
if (package.EntityGuid == null)
package.EntityGuid = Guid.Empty;
#endregion
using (StreamWriter stream = new StreamWriter(fileName))
{
try
{
stream.BaseStream.Seek(0, SeekOrigin.Begin);
stream.Write(JsonConvert.SerializeObject(package));
}
finally
{
stream.BaseStream.Flush();
stream.Close();
stream.Dispose();
}
}
in other place i get the file and use like this :
using (StreamReader stream = new StreamReader(fileName))
{
try
{
stream.BaseStream.Seek(0, SeekOrigin.Begin);
var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, DateFormatHandling = DateFormatHandling.MicrosoftDateFormat };
var sss = JsonConvert.DeserializeObject<SyncablePackage>(stream.ReadToEnd(), settings);
}
finally
{
stream.BaseStream.Flush();
stream.Close();
stream.Dispose();
}
}
and i get data in file :
{"EntityGuid":"4a6e20fd-e0c1-451b-870d-645162cbae60","Name":"AccessZones","Data":[{"ID":13,"Code":"23","Title":"(3)","Active":false,"Deleted":true,"Guid":"bdbc92c4-4246-4695-b36c-49b4911d7abe","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"},{"ID":15,"Code":"21","Title":" (2)","Active":false,"Deleted":true,"Guid":"b304aed4-d175-4d69-91ae-b98176e471f4","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"},{"ID":16,"Code":"22","Title":" (3)","Active":false,"Deleted":true,"Guid":"cdc03816-83e8-41dc-bd43-2681a3e3438c","ModificationDate":"2015-01-05T17:25:59.763","ModificationServerGuid":"33333333-3333-3333-3333-333333333333"}],"DataFileID":[-2147483648]}
can anyone tell me what i forgot ?
Your problem arises because you have declared Data as an object rather than its actual type:
public object Data;
When Json.NET tries to deserialize such a field, it has no information about the desired type, so deserializes it as a JToken (which is of course a subclass of object) to hold the data read from the file.
The simplest fix is to declare Data as its actual type:
public DataTable Data { get; set; }
If you can't do this (because e.g. you are actually using a typed table from a typed DataSet whose type might vary in runtime), the normal solution would be to set TypeNameHandling = TypeNameHandling.All. However, this doesn't work for DataTable because DataTable is stored via a converter, DataTableConverter -- and converters do not work with type name handling.
The solution to this is to create an intermediate generic proxy object that does not require a converter and can hold the type information:
public abstract class JsonTypeWrapper
{
protected JsonTypeWrapper() { }
[IgnoreDataMember]
public abstract object ObjectValue { get; }
public static object CreateWrapper<T>(T value)
{
if (value == null)
return new JsonTypeWrapper<T>();
var type = value.GetType();
if (type == typeof(T))
return new JsonTypeWrapper<T>(value);
// Return actual type of subclass
return Activator.CreateInstance(typeof(JsonTypeWrapper<>).MakeGenericType(type), value);
}
}
[DataContract]
public sealed class JsonTypeWrapper<T> : JsonTypeWrapper
{
public JsonTypeWrapper() : base() { }
public JsonTypeWrapper(T value) : base()
{
this.Value = value;
}
public override object ObjectValue
{
get { return Value; }
}
[DataMember]
public T Value { get; set; }
}
And then use it in your class like so:
[DataContract]
public partial class SyncablePackage
{
[IgnoreDataMember]
public object Data;
[DataMember(Name="Data")]
[JsonProperty("Data", TypeNameHandling = TypeNameHandling.All)] // REQUIRE output of type information.
object DataWrapper
{
get
{
if (Data == null)
return null;
return JsonTypeWrapper.CreateWrapper(Data);
}
set
{
var wrapper = value as JsonTypeWrapper;
if (wrapper == null)
{
if (value != null)
Debug.WriteLine("Invalid incoming data type: " + value.ToString());
Data = null;
return;
}
Data = wrapper.ObjectValue;
}
}
}
And the JSON produced would look something like:
{
"Data": {
"$type": "MyNamespace.JsonTypeWrapper`1[[MyNamespace.MyDataSetType+MyDataTableType, MyAssemblyName]], MyAssemblyName",
"Value": [
{
"Value": "2015-03-07T05:00:00Z"
},
]
}
}
Update
Just noticed you're also using ISerializable which makes things more complex, because JsonPropertyAttribute does not work with ISerializable. Instead, you must force serialization of the $type property in GetObjectData and in the streaming constructor:
[DataContract]
[Serializable]
public partial class SyncablePackage : ISerializable
{
public SyncablePackage() { }
public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
{
var jObj = (JObject)info.GetValue("Data", typeof(JObject));
if (jObj != null)
DataWrapper = jObj.ToObject<object>(new JsonSerializer { TypeNameHandling = TypeNameHandling.All });
Name = (string)info.GetValue("Name", typeof(string)); // Serialize your other properties as needed.
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Data", JObject.FromObject(DataWrapper, new JsonSerializer { TypeNameHandling = TypeNameHandling.All }));
info.AddValue("Name", Name); // Deserialize your other properties as needed.
}
[IgnoreDataMember]
public object Data;
[DataMember(Name = "Data")]
object DataWrapper
{
get
{
if (Data == null)
return null;
return JsonTypeWrapper.CreateWrapper(Data);
}
set
{
var wrapper = value as JsonTypeWrapper;
if (wrapper == null)
{
if (value != null)
Debug.WriteLine("Invalid incoming data type: " + value.ToString());
Data = null;
return;
}
Data = wrapper.ObjectValue;
}
}
}
Update 2
If you're also using the same code for binary serialization, you will need to ensure ISerializable.GetObjectData and public SyncablePackage(SerializationInfo info, StreamingContext ctxt) work the old way when doing binary formatting - despite being used for both binary formatting and JSON formatting. You can use StreamingContext.Context to signal to these methods that JSON is being serialized:
/// <summary>
/// Synthetic class to set in StreamingContext.Context
/// </summary>
public sealed class JsonStreamingContext
{
public JsonStreamingContext()
{
}
}
[DataContract]
[Serializable]
public partial class SyncablePackage : ISerializable
{
public SyncablePackage() { }
public SyncablePackage(SerializationInfo info, StreamingContext ctxt)
{
if (ctxt.Context is JsonStreamingContext)
{
var jObj = (JObject)info.GetValue("Data", typeof(JObject));
if (jObj != null)
DataWrapper = jObj.ToObject<object>(new JsonSerializer { TypeNameHandling = TypeNameHandling.All });
}
else
{
Data = (object)info.GetValue("Data", typeof(object));
}
Name = (string)info.GetValue("Name", typeof(string)); // Serialize your other properties as needed.
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (context.Context is JsonStreamingContext)
{
info.AddValue("Data", JObject.FromObject(DataWrapper, new JsonSerializer { TypeNameHandling = TypeNameHandling.All }));
}
else
{
info.AddValue("Data", Data);
}
info.AddValue("Name", Name); // Deserialize your other properties as needed.
}
// Rest as in first update.
}
Then serialize by setting a JsonStreamingContext in JsonSerializerSettings.Context.Context:
var settings = new JsonSerializerSettings { Context = new StreamingContext(StreamingContextStates.All, new JsonStreamingContext()) };
var json = JsonConvert.SerializeObject(package, settings);
var package = JsonConvert.DeserializeObject<SyncablePackage>(json, settings);
I have a List<object> with different types of objects in it like integers, strings, and custom types. All custom types are protobuf-adjusted.
What I wanna do now is to serialize / deserialize this list with protobuf.net. Up until now I suspect that I have to declare each and every type explicitly, which is unfortunately not possible with these mixed-list constructs. Because the binary formater has no problems to do these things I hope that I missed something and that you can help me out.
So my question is how to deal with objects in protobuf.net.
(disclosure: I'm the author of protobuf-net)
BinaryFormatter is a metadata-based serializer; i.e. it sends .NET type information about every object serialized. protobuf-net is a contract-based serializer (the binary equivalent of XmlSerializer / DataContractSerializer, which will also reject this).
There is no current mechanism for transporting arbitrary objects, since the other end will have no way of knowing what you are sending; however, if you have a known set of different object types you want to send, there may be options. There is also work in the pipeline to allow runtime-extensible schemas (rather than just attributes, which are fixed at build) - but this is far from complete.
This isn't ideal, but it works... it should be easier when I've completed the work to support runtime schemas:
using System;
using System.Collections.Generic;
using ProtoBuf;
[ProtoContract]
[ProtoInclude(10, typeof(DataItem<int>))]
[ProtoInclude(11, typeof(DataItem<string>))]
[ProtoInclude(12, typeof(DataItem<DateTime>))]
[ProtoInclude(13, typeof(DataItem<Foo>))]
abstract class DataItem {
public static DataItem<T> Create<T>(T value) {
return new DataItem<T>(value);
}
public object Value {
get { return ValueImpl; }
set { ValueImpl = value; }
}
protected abstract object ValueImpl {get;set;}
protected DataItem() { }
}
[ProtoContract]
sealed class DataItem<T> : DataItem {
public DataItem() { }
public DataItem(T value) { Value = value; }
[ProtoMember(1)]
public new T Value { get; set; }
protected override object ValueImpl {
get { return Value; }
set { Value = (T)value; }
}
}
[ProtoContract]
public class Foo {
[ProtoMember(1)]
public string Bar { get; set; }
public override string ToString() {
return "Foo with Bar=" + Bar;
}
}
static class Program {
static void Main() {
var items = new List<DataItem>();
items.Add(DataItem.Create(12345));
items.Add(DataItem.Create(DateTime.Today));
items.Add(DataItem.Create("abcde"));
items.Add(DataItem.Create(new Foo { Bar = "Marc" }));
items.Add(DataItem.Create(67890));
// serialize and deserialize
var clone = Serializer.DeepClone(items);
foreach (DataItem item in clone) {
Console.WriteLine(item.Value);
}
}
}
List<YourClass> list;
ProtoBuf.Serializer.Deserialize<List<YourClass>>(filestream);
There is a way of doing this, albeit not a very clean way, by using a wrapper object that utilises another serialisation mechanism that supports arbitrary objects. I am presenting an example below using JSON but, like I said, resorting to a different serialisation tool seems like defeating the purpose of using protobuf:
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
[DataMember(Order = 2)]
private readonly string _serialisedType;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
_serialisedContent = JsonConvert.SerializeObject(content);
_serialisedType = content.GetType().FullName;
Content = content;
}
[ProtoAfterDeserialization]
private void Initialise()
{
var type = Type.GetType(_serialisedType);
Content = type != null
? JsonConvert.DeserializeObject(_serialisedContent, type)
: JsonConvert.DeserializeObject(_serialisedContent);
}
}
EDIT: This can also be done using C#'s built-in binary serialisation
[DataContract]
public class ObjectWrapper
{
[DataMember(Order = 1)]
private readonly string _serialisedContent;
public object Content { get; private set; }
[UsedImplicitly]
private ObjectWrapper() { }
public ObjectWrapper(object content)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, content);
stream.Flush();
stream.Position = 0;
_serialisedContent = Convert.ToBase64String(stream.ToArray());
}
}
[ProtoAfterDeserialization]
private void Initialise()
{
var data = Convert.FromBase64String(source);
using (var stream = new MemoryStream(data))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(stream);
}
}
}