I use a package that contains code for a tcp server and client that is really easy to use, problem is it uses Newtonsoft Json serialization and deserialization with TypeNameHandling.All to send an receive messages, and client or servers could be untrusted sources.
public static JsonSerializerSettings JsonSettings = new()
{
TypeNameHandling = TypeNameHandling.All,
};
[...]
[...]
return JsonConvert.DeserializeObject<INetMessage>(message.Substring(8), JsonSettings);
It also contains such a class:
public class NetMessage<T> : INetMessage where T: ISafeNetSerialization
{
public ulong snowflake { get; set; }
public T Content { get; set; }
public NetMessage(T content)
{
this.Content = content;
}
public override string ToString()
{
return Content.ToString();
}
}
Can I use a code snippet using reflection, to go through all types inheriting INetMessage and ISafeNetSerialization interfaces and check if they can possibly contain something (or contain something that contains... etc) like an object, a dynamic or an Exception, a CollectionBase and other untyped objects and collections, including any Generic types inheriting those two that could be added in another library ?
I know that I should especially look for TempFileCollection and ObjectDataProvider.
Code snippet would then be used inside an unit test or at runtime before the initialization of the first server / and / or tcp client.
Related
I have never used Web API before, but I need a web service that will accept/return JSON objects and using this seemed like a reasonable thing. It looked pretty simple (if not a bit of overkill for my purposes), but a data structure I need to deal with looks something like:
{
"values":["foo", "bar"],
"default":"bar"
}
And so I went to make a Model object:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
Problem is that default seems to be a protected keyword. There must be some way to get around that, right?
You can use keywords in C# as identifiers by prepending # in front of them.
I would suggest to go different way. Keep your C# object model as much standard as possible (I wouldn't use # sign and C# keywords as property name).
We can separate the serialized (JSON) world and C# objects - just by using the Json.NET features.
One of the simpliest to use is decoration with Attribute:
[JsonProperty(PropertyName = "default")]
public string DefaultValue { get; set; }
In this case we have to reference Newtonsoft.Json in the project. If it must be POCO, we can introduce CustomResolver derrived from DefaultContractResolver and define these conversions there...
But separation of concern in this case is a bit more pure solution, I would say
EDIT: JSON Contract Resolver draft (see comments)
Important NOTE: Newtonsoft.Json is part of the Web API. Not only it is an open source, but even MS team bet on that as a core JSON serializer.
1) Newtonsoft.Json (as a part of the Web.API) is already installed in your solution. So you do not have to downloaded (nuget) separately. It would always be in your packages folder. So, to use the attribute is just adding the reference. It is there...
2) There is a small draft how to do the attribute stuff, while keeping the POCO. As I've tried explain here: POCO's, behavior and Peristance Igorance, to keep POCO (e.g. we do profit from layered Architecture with NHibernate on a data layer), we can replace attributes with a Contract Resolver. Our POCO library does not have to reference anything
We just have to do extend the service layer:
public class MyResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(
MemberInfo member,
MemberSerialization memberSerialization)
{
var jProperty = base.CreateProperty(member, memberSerialization);
var propertyInfo = member as PropertyInfo;
if (propertyInfo == null)
{
return jProperty;
}
// just adjust in case if Property name is DefaultValue
var isDefaultValueProeprty =
propertyInfo.Name.Equals("DefaultValue");
if(isDefaultValueProeprty)
{
jProperty.PropertyName = "default";
}
return jProperty;
}
...
This way we've provided the same information to serailizer as with the [JsonPropertyAttribute].
Now, we just have to use it. There are many ways (e.g. global) but we can do it for a controller only:
protected override void Initialize(HttpControllerContext context)
{
base.Initialize(context);
var jSettings = context.Configuration.Formatters.JsonFormatter.SerializerSettings;
jSettings.ContractResolver = MyResolver;
}
The class DropDownValues using camel convention:
class DropDownValues {
public string[] values { get; set; }
public string default { get; set; }
}
You can use prefix # to passby but it is still not following C# coding convention.
The better solution which you can both avoid reserved keyword and still use C# coding convention is using CamelCasePropertyNamesContractResolver:
class DropDownValues {
public string[] Values { get; set; }
public string Default { get; set; }
}
And customize JsonFormatter to avoid convention mismatch between C# and json object as below:
var jsonFormatter = configuration.Formatters.JsonFormatter;
jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
I have a library of fairly heavy-weight DTOs that is currently being used by some WCF services. We are attempting to bring it into protobuf-net world, with as little modification as possible. One particular set of items is giving me trouble in serialization. I'm going to simply them here because it gets a little complicated, but the gist of the problem is:
public class Key
{
public string Id {get; set;}
}
public class KeyCollection : IEnumerable<Key>
{
private readonly List<Key> list;
#region IEnumerable
// etc...
#endregion
}
public class Item
{
public long Id { get; set; }
}
public abstract class ContainerBase
{ }
public abstract class ContainerBase<T> : ContainerBase
where T : Item
{ }
public abstract class ContainerType1Base : ContainerBase<Item>
{
public KeyCollection Keys { get; set; }
}
public class ContainerType1 : ContainerType1Base
{ }
I've left out the decorators because I don't they're the problem, mostly because if I add void Add(Key item) { } to KeyCollection the whole thing seems to work. Otherwise, I run into problems attempting to serialize an instance of ContainerType1.
Actually, changing the signature of KeyCollection is kind of prohibitive, so I'm attempting to follow this answer to try to do it programatically. Specifically, setting itemType and defaultType to null on the "Keys" ValueMember of ContainerType1, ContainerType1Base and ContainerBase<Item>. I also set IgnoreListHandling to true on KeyCollection... which totally doesn't work. I get a generic "failed to deserialize" exception on the client, which I can post here if it would help. On the server side, I serialize it out using Serializer.Serialize(), and I spit out Serializer.GetProto<>() as well as JSON of the object, and they all seem to be work okay.
How can I turn off the list handling? Related to that, is there a way to turn on extra debugging while serializing to try to get some more information of the problem?
Fundamentally, the code shown looks fine. Unfortunately, there's currently a "feature" in gRPC that means that it discards the original exception when a marshaller (serializer) fails for some reason, so gRPC does not currently expose the actual problem. I have submitted a fix for this - it may or may not be accepted.
In the interim, I suggest that you simply remove gRPC from the equation, and simulate just the marshaller workload; to do this, on the server: generate the data you are trying to send, and do:
var ms = new MemoryStream();
Serializer.Serialize(ms, yourDataHere);
var payload = Convert.ToBase64String(ms.ToArray());
and obtain the value of payload (which is just a string). Now at the client, reverse this:
var ms = new MemoryStream(Convert.FromBase64String(thatStringValue));
Serialize.Deserialize<YourTypeHere>(ms);
My expectation here is that this should throw an exception that will tell you what the actual problem is.
If the gRPC change gets merged, then the fault should be available via:
catch (RpcException fault)
{
var originalFault = fault.Status.DebugException;
// ^^^
}
I have to work on a application when sends XML back. Because the root is always the "Reply", but the property's differ, deserializing can't be based on 1 object type.
I've now written code which loads the Xml first in a new XmlDocument, reads a Name attribute and based on the attribute, I try to deserialize it. Are there
better ways?
Example xml I can expect:
<Reply Name="GetModulesList" Result="yes"><ModuleName="xxxxxx.exe" Path="\Debug\xxxxxxx.exe" Order="1"/></Reply>
<Reply Name="OpenRecipe" Result="yes"/>
How whould you solve this?
XmlDocument doc = new XmlDocument();
doc.LoadXml(trimmedPart);
if (doc.DocumentElement?.Attributes != null)
{
XmlAttribute name = doc.DocumentElement.Attributes.Cast<XmlAttribute>().SingleOrDefault(a => String.Compare(a.Name, "name", StringComparison.OrdinalIgnoreCase) == 0);
replyName = name?.Value;
}
if (!string.IsNullOrEmpty(replyName))
{
XmlSerializer serializer = new XmlSerializer(GetSerializerObjectType(replyName));
using (StringReader reader = new StringReader(trimmedPart))
{
object obj = serializer.Deserialize(reader);
if (obj != null) returnList.Add(obj);
}
}
I recently dealt with a similar scenario. Here's a very rough outline, adapted from what I was working with to your case as specifically as I can.
My assumption is that you're going to do different things with different types. If it's an OpenRecipe you're going to do one thing, if it's GetModuleList you're going to do something else.
The biggest difference is that each "handler" I worked with for different types might handle completely different types of data - XML, JSON, even Excel, so the handlers received content in the form of a byte array and were responsible for deserializing it. (It wasn't my idea to write an in-house version of Biztalk.)
In this case, if it's always XML, you can
Determine the type of the inner XML from the Name attribute
Deserialize the inner XML to that type
Pass it off to a strongly-typed class
This class is concerned with deserializing the Reply (except for the unknown inner content) and passing it to something that will handle that inner content in a more strongly typed way:
public class XmlReplyRouter
{
private readonly IReplyTypeMapper _replyTypeMapper;
private readonly IHandlerFactory _handlerFactory;
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(Reply));
public XmlReplyRouter(
IReplyTypeMapper replyTypeMapper,
IHandlerFactory handlerFactory)
{
_replyTypeMapper = replyTypeMapper;
_handlerFactory = handlerFactory;
}
public void RouteReply(string replyXml)
{
using (var reader = new StringReader(replyXml))
{
var reply = (Reply)_serializer.Deserialize(reader);
var replyType = _replyTypeMapper.GetReplyType(reply.Name);
var handler = _handlerFactory.GetHandler(replyType);
handler.HandleReply(reply);
}
}
}
public class Reply
{
[XmlAttribute]
public string Name { get; set; }
[XmlAttribute]
public string Result { get; set; }
[XmlAnyElement]
public XmlNode InnerXml { get; set; }
public string XmlContent => InnerXml?.OuterXml;
}
That's where all the ugliness goes.
The XmlAnyElement attribute allows us to deserialize without knowing what to make of that inner content. Once we know what type to use we can separately deserialize that.
The implementations of IReplyTypeMapper and IHandlerFactory could be anything. In my case I couldn't use a DI container so it contained a Dictionary<string, IReplyHandler> and selected the correct one based on the name. (The terminology I worked with was different, but same concept.)
The first determines the type (from the "name" attribute) and the second returns a handler for that type.
public interface IReplyTypeMapper
{
Type GetReplyType(string replyName);
}
public interface IHandlerFactory
{
IReplyHandler GetHandler(Type contentType);
}
Finally, here's the interface and a base class for the reply handlers.
As you can see, the base class doesn't really do anything. It just bridges the gap between object and generic types so that we can write strongly-typed handlers.
public interface IReplyHandler
{
void HandleReply(object content);
}
public abstract class BaseHandler<T> : IReplyHandler
{
private readonly XmlSerializer _serializer = new XmlSerializer(typeof(T));
public void HandleReply(object content)
{
HandleReplyContent((T)content);
}
protected abstract void HandleReplyContent(T content);
}
As you can see it's very similar to what you're doing. The objective, though, is that once I've gotten this minimal amount of initial code out of the way, everything else is a strongly-typed class like this:
public class SomeSpecificReplyHandler : BaseHandler<SomeSpecificReply>
{
// Maybe these have dependencies of their own. That's easiest if
// everything gets resolved from an IoC container.
protected override void HandleReplyContent(SomeSpecificReply content)
{
// do whatever
}
}
I don't know if this might be overkill for your needs. My intent was to start from an entry point where my data could be just about anything and quickly get out of there to a place where all of my code was strongly-typed and easy to test. All of these classes are testable (I wrote a few while typing this up) the individual handlers would also be easy to test.
This post contains some other details about implementing the factories without an IoC container. In my case I couldn't work with an IoC container, so I had to "manually" compose all of my classes. The inner implementation of the factory was just a dictionary. That way if I had the option of switching to an IoC container later I could replace the whole thing with DI registrations.
How come when I send a message to RabbitMQ through rebus that it is adding extra object data like below, primarily the $type.
{"$type":"ThreeSquared.VTGPAM.Objects.Wagon, ThreeSquared.VTGPAM.Objects","WagonId":"a98a06ab-33b9-4a11-9de2-df0b8787b713","WamosId":12324,"Description":"test","YearBuilt":1982,"Token":"3cce443c-249f-4fd2-9882-5830fb308b6b"}
We have a client that will just be using the Java RabbitMQ library with no rebus. This approach I believe we just send the JSON without the type declarations. This therefore doesn't work when I try and read in simple JSON object. How can we make it work so that it doesn't define the $type in the message?
It's simply because Rebus by default uses Newtonsoft JSON.NET with TypeNameHandling.All, which means that the $type field is included in every serialized object containing the full .NET type name of the type serialized.
The benefit is that you can serialize almost anything, even though it may contain instances referenced by (possibly abstract) supertypes, and even by interfaces.
E.g. this command message type
public class ProcessFile
{
public ProcessFile(string filePath, IEnumerable<IFileProcessingTask> tasks)
{
FilePath = filePath;
Tasks = tasks;
}
public string FilePath { get; }
public IReadOnlyCollection<IFileProcessingTask> Tasks { get; }
}
could contain arbitrary implementations of IFileProcessingTask, e.g. something like
public class GZipFileProcessingTask : IFileProcessingTask
{
// ...
}
as long as the recipient can find the type by looking it up via the value of the $type field.
If you want to process this type of message on another platform, you can simply make it ignore the $type field of every object. This may be easy/hard/impossible, depending on how flexible your JSON serializer is.
Another option is to simply replace Rebus' serializer with your own implementation by doing this
Configure.With(...)
.(...)
.Serialization(s => s.UseCustomJsonSerialization())
.Start();
where UseCustomJsonSerialization is an extension method that you implement like this:
public static class RebusConfigEx
{
public static void UseCustomJsonSerialization(this StandardConfigurer<ISerializer> configurer)
{
configurer.Register(c => new YourCustomJsonSerializer());
}
}
and then all there is left to do is to create the class YourCustomJsonSerializer as an implementation of ISerializer.
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)