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
Related
I'm struggling migrating from protobuf-net v2.4.6 to v3.0.100 (or any 3.0.x) in regards to an existing type hierarchy used as ProtoContracts with one of the subtypes requiring a surrogate due to one of its property being of type object.
With previous configuration in place, I get the following exception thrown on creating the runtime model:
System.InvalidOperationException: 'Types with surrogates cannot be used in inheritance hierarchies'
Hence, my question is how to properly deal with this scenario using protobuf-net 3.0.x?
Here's my (over-)simplified repro of the issue:
class Program
{
static void Main(string[] args)
{
var model = RuntimeTypeModel.Create();
_ = model[typeof(Base)]; // <-- InvalidOperationException thrown here
Base value = new Complex();
var copy = model.DeepClone(value);
}
}
[ProtoContract]
[ProtoInclude(1, typeof(Simple))]
[ProtoInclude(2, typeof(Complex))]
public abstract class Base
{
}
[ProtoContract]
public class Simple : Base
{
}
[ProtoContract(Surrogate = typeof(ComplexSurrogate))]
public class Complex : Base
{
}
[ProtoContract(Name = nameof(Complex))]
public class ComplexSurrogate
{
[ProtoConverter]
public static ComplexSurrogate Convert(Complex source) => new ComplexSurrogate();
[ProtoConverter]
public static Complex Convert(ComplexSurrogate source) => new Complex();
}
As a side note: When compiling protobuf-net from source with the above mentioned exception suppressed, I'm able to defined a surrogate for the Base class which seems to serve as a workaround.
Right now, that scenario isn't supported. While reworking the code for v3, some ambiguous outcomes/intents were found, and it needs work to go in and figure out what the correct outcomes are in each case, design how to achieve that, implement it, and test it. That time has not yet been found, so right now it is safer to prevent a configuration that could lead to big problems downstream, than to just shrug and assume that whatever happens is correct. It is on my list of things to do, but: ultimately this is a project that comes entirely out of my own spare time - it isn't sponsored or part of my paid work, so: it'll get there when it gets there.
I encountered the same error in protobuf v3, and I solved that with custom serializer.
My base class is
[ProtoContract]
[ProtoInclude(500, typeof(XXXRequest))]
[ProtoInclude(501, typeof(XXXResponse))]
// ...
public class MessageBase
{
[ProtoMember(1)]
long ID { get; internal set; }
[ProtoMember(3)]
int ExecutionMilliseconds { get; set; }
}
Its equivalent proto is
message Message {
int64 ID = 1;
int32 ExecutionMilliseconds = 3;
oneof body {
PredictBonusRequest XXXRequest = 500;
PredictBonusResponse XXXResponse = 501;
// ...
}
}
I want to replace some types (e.g. XXXResponse) to use the contract-first class instead. This would allow us to migrate from code-first to contract-first smoothly.
For sub-types should be surrogated, we create custom serializer as below.
using ProtoBuf;
using ProtoBuf.Serializers;
using UnderlyingMessage = GeneratedProto.Contract.Message;
using UnderlyingResponse = GeneratedProto.Contract.XXXResponse;
[DataContract]
[Serializable]
[ProtoContract(Serializer = typeof(XXXResponseSerializer))]
public class XXXResponse : MessageBase
{
class XXXResponseSerializer : ISerializer<XXXResponse>
{
public SerializerFeatures Features => SerializerFeatures.CategoryMessage | SerializerFeatures.WireTypeString;
public XXXResponse Read(ref ProtoReader.State state, XXXResponse value)
{
ISerializer<UnderlyingMessage> serializer = state.GetSerializer<UnderlyingMessage>();
return serializer.Read(ref state, value);
}
public void Write(ref ProtoWriter.State state, XXXResponse value)
{
ISerializer<UnderlyingMessage> serializer = state.GetSerializer<UnderlyingMessage>();
serializer.Write(ref state, value);
}
}
private readonly UnderlyingResponse _resp;
public XXXResponse() : this(new UnderlyingResponse() { })
{
}
private XXXResponse(UnderlyingResponse msg)
{
_resp = msg;
}
public static implicit operator XXXResponse(UnderlyingMessage value)
{
if( value != null)
{
return new XXXResponse(value.XXXResponse)
{
ID = value.ID,
ExecutionMilliseconds = value.ExecutionMilliseconds,
};
}
return null;
}
public static implicit operator UnderlyingMessage(XXXResponse value)
{
if(value != null)
{
return new UnderlyingMessage()
{
ID = value.ID,
ExecutionMilliseconds = value.ExecutionMilliseconds,
XXXResponse = value._resp,
};
}
return null;
}
public Transaction[] Transactions
{
get { return _resp.Transactions?.Select(t => (Transaction)t)?.ToArray(); }
set { _resp.Transactions = value?.Select(t => (BE.Data.Contract.Transaction)t)?.ToList(); }
}
public long DomainID { get { return _resp.DomainID; } set { _resp.DomainID = value; } }
public string UniversalID { get { return _resp.UniversalID; } set { _resp.UniversalID = value; } }
public string ExtraData { get { return _resp.ExtraData; } set { _resp.ExtraData = value; } }
// other proxied fields ...
}
The key is, when ISerializer.Read or ISerializer.Write is fired, the wire-format is from the scope of the entrie message, including all fields of base class, and current sub-type is in a field whose number is identified by ProtoInclude.
In our case this works. For other sub-types which we don't want surrogate at this moment, it still works as it did.
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!
Using Json.Net, I am trying to pass a JsonSerializerSettings object as one of the parameters of an Action.
The SerializerSettings is in assembly A:
public static class SerializerSettings
{
public static readonly JsonSerializerSettings Jss = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Error,
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>
{ new StringEnumConverter(), new NullStringConverter() }
};
}
(the NullStringConverter is just a custom converter inheriting from JsonConverter)
for brevity:
public class NullStringConverter : JsonConverter
{}
I may not be using the best or proper terminology to describe this but, then from assembly B the method defined below is passed as a pointer to assembly A and there in assembly A it is invoked with its parameters that include the JsonSerializerSettings defined above.
private static void Investigated<TRequest, TResponse>(string data, JsonSerializerSettings jss) where TRequest : IBaseRequest where TResponse : IBaseResponse
{
if (string.IsNullOrEmpty(data) || jss == null)
Console.WriteLine("Bad Data");
else
{
var response = JsonConvert.DeserializeObject<TResponse>(data, jss);
}
}
In debugger i see the data string is good, jss is properly set, but the deserialization completes with all values in the 'response' object being null or default values.
If instead i move the same JsonSerializerSettings local to assembly B, deserialization comes out fine...
I even tried to create a new JsonSerializerSettings FROM the one passed in, but that had the same null/default results.
My first inclination is to suspect it is not possible pass/use a JsonSerializerSettings from one assembly to/in another in this way, a static object as a action's parameter?
Might i be doing anything obviously wrong.
I apologia in advance to anyone feeling my question is not well formed, lacks information, or otherwise answered elsewhere, im trying to follow the rules...
1. JsonConvert returns nulls or defaults
The kind symptoms that you described that results your JsonConvert.DeserializeObject() method returning an object with nulls or defaults, occurs when you do not map your objects properly. Here is a small example to illustrate it.
class Foo
{
public int Id { get; set; }
public string Name { get; set; }
}
class Bar
{
public int BarId { get; set; }
public string BarName { get; set; }
}
void Main()
{
var foo = new Foo()
{
Id = 1,
Name = "One"
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
var bar = Newtonsoft.Json.JsonConvert.DeserializeObject<Bar>(json);
}
Here is the result of bar:
bar = { BarId = 0, BarName = null }
Incorrect mapping will result in nulls and defaults.
2. Pay attention to details when using a custom JsonConverter
Going further with the example above, when using a JsonConverter you also have to be aware of the mapping as well, as it's more prone to produce the same kind of problems from small typos or other errors.
MyCustomJsonSerializerSettings
public static class MyCustomJsonSerializerSettings
{
private static JsonSerializerSettings _jss;
public static JsonSerializerSettings Jss
{
get { return _jss; }
private set { _jss = value; }
}
static MyCustomJsonSerializerSettings()
{
_jss = new JsonSerializerSettings
{
MissingMemberHandling = MissingMemberHandling.Error,
NullValueHandling = NullValueHandling.Ignore,
Converters = new List<JsonConverter>
{
new FooToBarConverter()
}
};
}
}
FooToBarConverter
public class FooToBarConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
Foo foo = (Foo)value;
JObject jo = new JObject();
jo.Add("BarId", new JValue(foo.Id)); // make sure of your mapping!
jo.Add("BarName", new JValue(foo.Name)); // make sure of your mapping!
jo.WriteTo(writer);
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Make sure that you are properly mapping the object in the custom converter.
Putting it all together...
void Main()
{
var foo = new Foo()
{
Id = 1,
Name = "One"
};
string json = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
var bar = Newtonsoft.Json.JsonConvert.DeserializeObject<Bar>(json); // nulls/defaults
JsonSerializerSettings jss = MyCustomJsonSerializerSettings.Jss;
string json2 = Newtonsoft.Json.JsonConvert.SerializeObject(foo, jss); // note jss
var bar2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Bar>(json2); // as expected!
}
3. Portability of the Converter Between Assemblies
To be 100% certain that the converters will get passed along with your settings, you can always do something like this
public class MyCustomJsonSerializerSettings // not a static class
{
private static JsonSerializerSettings _jss;
public static JsonSerializerSettings Jss
{
get { return _jss; }
private set { _jss = value; }
}
static MyCustomJsonSerializerSettings()
{
_jss = new JsonSerializerSettings { ... };
}
private class FooToBarConverter : JsonConverter // note that it is contained within
// the class
{
...
}
}
I have this json structure:
[{"trace":{"details":{"date":"[28-02-2016 11:04:26.856573]","type":"[info]","message":"[system done.]"},"context":{"context":[[{"ID":"john dillinger"}]]}}},{"trace":{"details":{"date":"[28-02-2016 11:04:26.856728]","type":"[info]","message":"[trace done.]"},"context":{"context":[[{"ID":"john dillinger"}]]}}}]
I can deserialize it correctly using this class:
public class Trace
{
public TraceValue trace;
}
public class TraceValue
{
public Details details;
public Context context;
}
public class Details
{
public String date;
public String type;
public String message;
}
public class Context
{
public List<List<IdItem>> context;
}
public class IdItem
{
public String ID;
}
an example of deserialization:
var response = "json above";
var obj = JsonConvert.DeserializeObject<List<Trace>>(response);
now the problem is that sometimes the json is returned with this structure:
{
"success": false,
"message": "No content."
}
my code fall in error 'cause the structure is different. I tried to read the header but is returned as 200 'cause no error happean. So how can I recognize the different structure and avoid the json exception? This is the class to manage the different json structure:
public class RootObject
{
public bool success { get; set; }
public string message { get; set; }
}
I could do this in the exception but this isn't a good practice, there is another way?
In a case like yours, the better is to first obtain a JToken and then check if it has the message property and deserialize to the correct object:
var container = (JContainer)Newtonsoft.Json.JsonConvert.DeserializeObject(response);
var message = container["message"];
if(message == null)
{
var obj = container.ToObject<List<Trace>>();
//Do whatever you need to do with the object
}
else
{
var msg = container.ToObject<RootObject>();
//Do whatever you need to do with the object
}
Not a proper way but I have done this.
Convert the generated json object in to string. And edit the string to make the same format.
Than you can parse both string which are in same format
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);
}
}
}