Protobuf.Net: Serializing List<Uri> throws "Unable to cast object of type 'System.Uri' to type 'System.String'" - serialization

The title says it all, Protobuf.net correctly serializes Uri, but won't serialize List<Uri>
[ProtoContract]
class SingleUri
{
[ProtoMember(1)]
public Uri Uri { get; set; }
}
[ProtoContract]
class MultiUri
{
[ProtoMember(1)]
public List<Uri> Uris { get; set; }
}
static void Main(string[] args)
{
var single = new SingleUri { Uri = new Uri("http://www.google.com") };
using (var file = File.Create("single.bin"))
// Works a treat
Serializer.Serialize(file, single);
var multi = new MultiUri { Uris = new List<Uri> { single.Uri } };
using (var file = File.Create("multi.bin"))
// fails with System.InvalidCastException: Unable to cast object
//of type 'System.Uri' to type 'System.String'.
Serializer.Serialize(file, multi);
}
I'm running this against the latest NuGet package as of time of writing.
Can anyone tell me what I'm missing here? Any workaround for this?
Thanks.

Related

Aggregate Exception Asp.Net Core 5.0

I have an Mvc project which is based on Asp.Net Core 5.0 . I have my own Core Layer and i have my own Photo,Video uploader method which is based my FileRepo class.
Here is my FileModel class:
public class FileModel
{
public int FileID { get; set; }
public string FileName { get; set; }
public string FileType { get; set; }
public string FileExtension { get; set; }
public string FileSlug { get; set; }
public string FilePath { get; set; }
public byte[] Data { get; set; }
}
Here is my File Uploader method :
public interface IFileUploader
{
Task<FileModel> FileUploadToDatabase(List<IFormFile> files);
Task<FileModel> FileUploadToPath(List<IFormFile> files);
Task<bool> FileDeleteFromPath(int id);
}
public class FileUploader : IFileUploader
{
private FileModel _fileModel;
public FileUploader(FileModel fileModel)
{
_fileModel = fileModel;
}
public async Task<FileModel> FileUploadToDatabase(List<IFormFile> files)
{
foreach (var file in files)
{
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
var extension = Path.GetExtension(file.FileName);
_fileModel = new FileModel
{
FileName = fileName,
FileType = file.ContentType
};
using (var dataStream = new MemoryStream())
{
await file.CopyToAsync(dataStream);
_fileModel.Data = dataStream.ToArray();
}
}
return _fileModel;
}
public async Task<FileModel> FileUploadToPath(List<IFormFile> files)
{
foreach (var file in files)
{
var basePath = Path.Combine(Directory.GetCurrentDirectory() + "\\Files\\");
bool basePathExists = Directory.Exists(basePath);
if (!basePathExists) Directory.CreateDirectory(basePath);
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
var filePath = Path.Combine(basePath, file.FileName);
var extension = Path.GetExtension(file.FileName);
if (!File.Exists(filePath))
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(stream);
}
_fileModel = new FileModel
{
FileName = fileName,
FileType = file.ContentType,
FilePath = filePath
};
}
}
return _fileModel;
}
}
As u guys can see,its a different layer and there is nothing related with my Mvc project.
Im getting error when i add those extension to my Mvc project.
The error says me that :
'Some services are not able to be constructed (Error while validating the service descriptor
'ServiceType: CORE_HBKSOFTWARE.Interfaces.IFileUploader Lifetime: Singleton ImplementationType:
CORE_HBKSOFTWARE.Classes.FileUploader': Unable to resolve service for type
'CORE_HBKSOFTWARE.Models.FileModel' while attempting to activate
'CORE_HBKSOFTWARE.Classes.FileUploader'.)'
I dont know why im getting that error. That FileModel class is seems to okey to me and i still dont know. I allready add my services.AddSingleton<IFileUploader, FileUploader>(); to my Startup.cs .
Thanks for any suggestion !
If you want to use constructor injection to create an instance of FileModel in your FileUploader class you need to register it to the IoC Container. You do that by calling following method in the ConfigureServices method:
services.AddSingleton<FileModel>();
You can also choose a different lifetime by calling AddScoped or AddTransient.

Deserializing Enum from DescriptionAttribute using custom JSON.NET JsonConverter stopped working

Looking for help with Newtonsoft Json on asp.net core 2.2.
I have a JsonEnumConverter<T> which was responsible for serializing/deserializing values from DescriptionAttribute from an Enum type. It was working fine until about 2 weeks ago and now it has completely stopped working.
here's what I have:
//From PerformersController:
public async Task<ActionResult<PagedPerformers>> GetPagedPerformersAsync([FromQuery] PerformerRequest performerRequest) { ... }
[JsonObject]
public class PerformerRequest : PageRequest
{
[FromQuery(Name = "performer_id")]
[JsonProperty(PropertyName = "performer_id", Order = 1)]
public override string Id { get; set; }
....
}
[JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
public enum SortDirectionType
{
[Description("asc")]
ASCENDING,
[Description("desc")]
DESCENDING
}
public abstract class PageRequest
{
[FromQuery(Name = "page")]
[JsonProperty("page")]
public int Page { get; set; }
[FromQuery(Name = "limit")]
[JsonProperty("limit")]
public int PageSize { get; set; } = 100;
[FromQuery(Name = "sort_field")]
[JsonProperty("sort_field")]
public string SortField { get; set; } //= "Id";
[FromQuery(Name = "sort_dir")] [JsonConverter(typeof(JsonEnumConverter<SortDirectionType>))]
[JsonProperty("sort_dir")]
public SortDirectionType SortDirection { get; set; }
[FromQuery(Name = "id")]
[JsonProperty("id")]
public virtual string Id { get; set; }
}
public class JsonEnumConverter<T> : JsonConverter where T : struct, IComparable, IConvertible, IFormattable
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
var enumDescription = (string)reader.Value;
return enumDescription.GetEnumValueFromDescription<T>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var type = typeof(T);
if (!type.IsEnum) throw new InvalidOperationException();
if (value != null)
{
if (value is Enum sourceEnum)
{
writer.WriteValue(sourceEnum.GetDescriptionFromEnumValue());
}
}
}
}
public static class EnumExtensions
{
public static string GetDescriptionFromEnumValue(this Enum #enum)
{
FieldInfo fi = #enum.GetType().GetField(#enum.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return #enum.ToString();
}
public static T GetEnumValueFromDescription<T>(this string description)
{
var type = typeof(T);
if (!type.IsEnum)
throw new InvalidOperationException();
foreach (var field in type.GetFields())
{
if (Attribute.GetCustomAttribute(field,
typeof(DescriptionAttribute)) is DescriptionAttribute attribute)
{
if (attribute.Description == description)
return (T)field.GetValue(null);
}
else
{
if (field.Name == description)
return (T)field.GetValue(null);
}
}
throw new ArgumentException($"No matching value for enum {nameof(T)} found from {description}.",$"{nameof(description)}"); // or return default(T);
}
}
this was working absolutely fine until recently. Now I'm not sure whats going on I get ValidationProblemDetails response right away. If I suppress asp.net core 2.2 model state invalid filter then modelState.IsValid will still have false. If I put a breakpoint in ReadJson of my JsonEnumConverter it wont even hit. Even tried to set JsonSerializerSettings in startup with no success or luck. Have already tried replacing Description with EnumMember and StringEnumConverter. Still the same issue. Seems like there is some issue with ModelBinder and Json.NET not playing well with each other.
NOTE: This issue is happening on ASP.NET Core 2.2. Suggesting solutions for 3.0 is not helpful!!
If you are using aspnet core 3 / netstandard 2.1, you can try this library https://github.com/StefH/System.Text.Json.EnumExtensions which defines some extensions to the JsonStringEnumConverter to support attributes like EnumMember, Display and Description.

Deserialization of incoming message

Json is sent from the client to my api controller
Message
{"todo":{"title":"jo","isCompleted":false,"isDeleted":false,"testFK":null}}
I will list different consuming server side methods that should receive this message transformed into a object
public void Post(TaskEntity todo)
{
using (var context = new ToDoEntities())
{
context.Tasks.Add(todo.ToEf());
context.SaveChanges();
}
}
todo parameter is null.
public void Post(UpdateTodoInputMessage message)
{
using (var context = new ToDoEntities())
{
context.Tasks.Add(todo.todo.ToEf());
context.SaveChanges();
}
}
Message class
namespace MvcApplication1.Messages
{
[DataContract]
public class UpdateTodoInputMessage
{
[DataMember]
public TaskEntity todo { get; set; }
}
}
The todo property of the message class is null.
If I change the post method to a string then my client receives a 405 Method not allowed.
public void Post(string message)
{
using (var context = new ToDoEntities())
{
// context.Tasks.Add(todo.todo.ToEf());
// context.SaveChanges();
}
}
I am at a loss and would love some assistance as to how the deserialization of the string is taking place, and why I am getting these strange results.
The reason was that my Task Entity class didn't have a empty constructor

What's the best way to serialize an array based on an interface in WCF?

First the code:
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[OperationContract]
public string Expression { get; set; }
[OperationContract]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
A dispatcher encodes data as xml and sends it to an IWorker instance where each expression is evaluated. When an IWorker instance evaluates an expression as true, IAction.Execute is called and the xml/data is passed.
What's the best way to serialize Rule.Actions? I've started writing a custom serializer but I'd prefer to see if there is an easier way.
Thanks.
I dont think you can use interfaces in DataContracts (someone correct me if im wrong, but i assume thats like trying to use a generic too). What I do, is have a parent class, then add the KnownType attribute. For instance
[DataContract]
public class Action
{
//members and properties
}
[DataContract]
public class SomeOtherAction:Action
{
//more implimentation
}
[DataContract]
[KnownType(typeof(SomeOtherAction))]
public class Rule
{
[DataMember]
List<Action> Actions{get;set;}
}
Now you can stuff any object that inherits from the parent Action object in to the Actions list, and it will properly serialize all their respective class properties (as long as the object is listed as a knowntype).
*I used "Action" name as an example to relate to yours, obviously Action is a keyword in .NET
Serialization is the process of converting between an object data and bytes which can be transferred over the wire. Interfaces define behavior, so by default WCF can't serialize such data. If you have the exact same assemblies on the client and the server, however, you can use the NetDataContractSerializer, which will essentially serialize (and be able to serialize) all the type information for the objects being serialized, so it can be recreated at the other side.
The code below shows how to use the NetDataContractSerializer in a service for that (based on the main example for this, the post from Aaron Skonnard at http://www.pluralsight-training.net/community/blogs/aaron/archive/2006/04/21/22284.aspx)
public class StackOverflow_6932356
{
[ServiceContract]
public interface IWorker
{
[OperationContract]
void Process(XmlElement data);
[OperationContract]
void Update(Rule rule);
}
[DataContract]
public class Rule
{
[DataMember]
public string Expression { get; set; }
[DataMember]
public List<IAction> Actions { get; set; }
}
public interface IAction
{
void Execute(XmlElement data);
}
public class Service : IWorker
{
static List<IAction> AllActions = new List<IAction>();
public void Process(XmlElement data)
{
foreach (var action in AllActions)
{
action.Execute(data);
}
}
public void Update(Rule rule)
{
AllActions = rule.Actions;
}
}
public class Action1 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
public class Action2 : IAction
{
public void Execute(XmlElement data)
{
Console.WriteLine("Executing {0} for data: {1}", this.GetType().Name, data.OuterXml);
}
}
class NetDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
public NetDataContractSerializerOperationBehavior(OperationDescription operationDescription)
: base(operationDescription) { }
public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
{
return new NetDataContractSerializer(name, ns);
}
}
static void ReplaceDCSOB(ServiceEndpoint endpoint)
{
foreach (var operation in endpoint.Contract.Operations)
{
for (int i = 0; i < operation.Behaviors.Count; i++)
{
if (operation.Behaviors[i] is DataContractSerializerOperationBehavior)
{
operation.Behaviors[i] = new NetDataContractSerializerOperationBehavior(operation);
break;
}
}
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(IWorker), new BasicHttpBinding(), "");
ReplaceDCSOB(endpoint);
host.Open();
Console.WriteLine("Host opened");
var factory = new ChannelFactory<IWorker>(new BasicHttpBinding(), new EndpointAddress(baseAddress));
ReplaceDCSOB(factory.Endpoint);
var proxy = factory.CreateChannel();
proxy.Update(new Rule
{
Expression = "Expr",
Actions = new List<IAction> { new Action1(), new Action2() }
});
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><foo>bar</foo></root>");
proxy.Process(doc.DocumentElement);
((IClientChannel)proxy).Close();
factory.Close();
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}

Serializing object graph using MongoDB Bson serializer

I've been playing a little with the MongoDB Bson serializer, using the following piece of code:
class Program
{
public class myValue
{
public int Id = 0;
public string Label = "";
}
public class myValueMap : Dictionary<string, myValue>
{
}
public class myProdData
{
public myValueMap Mapping { get; set; }
}
public class mySystemPosition
{
public string Text { get; set; }
public myProdData ProdData { get; set; }
}
static void Main(string[] args)
{
BsonClassMap.RegisterClassMap<mySystemPosition>();
BsonClassMap.RegisterClassMap<myProdData>();
BsonClassMap.RegisterClassMap<myValueMap>();
BsonClassMap.RegisterClassMap<myValue>();
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new myValueMap()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var bson = o.ToBson();
var text = Encoding.ASCII.GetString(bson);
}
}
however I don't seem to be able to get the myProdData.Mapping serialized....
Do I need to configure the MongoDB Bson serializer in a special way, to make this work?
You no need to use BsonClassMap.RegisterClassMap if you no need custom serializtion(documentation).
All your classes will be desirialzied according to default rules.
Also i am changed your example a little bit to get it work(i've replaces myValueMap class with Dictionary):
public class myProdData
{
public Dictionary<string, myValue> Mapping { get; set; }
}
static void Main(string[] args)
{
var o = new mySystemPosition()
{
ProdData = new myProdData()
{
Mapping = new Dictionary<string, myValue>()
{
{"123", new myValue() {Id = 1, Label = "Item1"}},
{"345", new myValue() {Id = 2, Label = "Item2"}},
}
}
};
var json = o.ToJson();
Console.WriteLine(json);
Console.ReadKey();
}
Here is console output(just well formatted):
{
"Text":null,
"ProdData":{
"Mapping":{
"123":{
"_id":1,
"Label":"Item1"
},
"345":{
"_id":2,
"Label":"Item2"
}
}
}
}
You can test your serializtion using ToJson() extention method, in order to view that all correct and after that use ToBson() if need.
The problem is that myValueMap derives from Dictionary. That results in a class that the AutoMap method can't handle.
I recommend you just use the Dictionary directly, as Andrew did in his reply.
Ufortunately the myValueMap is an object that I can't easily change, however it turns out, that's pretty easy to create your own (de)serializer....
public class myValueMapSerializer : IBsonSerializer
{
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, System.Type actualType, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var res = new myValueMap();
var ser = new DictionarySerializer<string, myValue>();
var dic = (Dictionary<string, myValue>)ser.Deserialize(bsonReader, typeof(Dictionary<string, myValue>), options);
foreach (var item in dic)
{
res.Add(item.Key, item.Value);
}
return res;
}
public object Deserialize(Bson.IO.BsonReader bsonReader, System.Type nominalType, IBsonSerializationOptions options)
{
throw new Exception("Not implemented");
}
public bool GetDocumentId(object document, out object id, out IIdGenerator idGenerator)
{
id = null;
idGenerator = null;
return false;
}
public void Serialize(Bson.IO.BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
if (nominalType != typeof(myValueMap)) throw new ArgumentException("Cannot serialize anything but myValueMap");
var ser = new DictionarySerializer<string, myValue>();
ser.Serialize(bsonWriter, typeof(DictionarySerializer<string, myValue>), value, options);
}
public void SetDocumentId(object document, object id)
{
return;
}
}