Issue returning a JSON associative array from an ASP.NET Core Web API - json.net

I have a Web API method in ASP.NET Core 3.1 that returns an enumerable list of objects:
public async Task<IEnumerable<MyObject>> Get()
The Web API returns JSON by default. This has worked fine, until I added a property of type Dictionary<int, object> to MyObject, not realizing that whatever serializer ASP.NET Core 3.1 is using to build the response can't serialize a Dictionary (error is "type Dictionary<int, object> is not supported"). I can reproduce the same error by trying to serialize the Dictionary using the new System.Text.Json library, which is what I'm guessing is being used by the web API to build the JSON response.
Since JsonConvert still serializes Dictionary just fine, it wouldn't be hard to do the serialization manually in the method. But that means making my own JSON response and returning it as a content string, which just seems ... not great.
Is there another associative array type I could be using that the web API can serialize into JSON correctly? Or is there a way to configure the web API to use a JSON serialization library that can handle Dictionary? Or am I just stuck making my own JSON response for this method?
Edit: To be clear, I am curious which of these are possible, what the advantages or disadvantages are to each, so that I might choose a good solution for my circumstance.

This answer comes from #dbc in the comments. I hadn't realized that the System.Text.Json difficulty with serializing Dictionary wasn't with Dictionary generally, but specifically with Dicitonary using a non-string key. It was trivial for us to write a converter to translate the necessary Dictionary to a string-keyed type, and then the built-in JSON serialization handled it just fine.
Thanks, #dbc!

Related

(de)Serializing stream using System.Text.Json

I am trying to serialize an object into a MemoryStream using System.Text.Json's JsonSerializer. I am unable to find the implementation/method of that in the documentation. Can someone share the sample implementation for serialization and deserialization using System.Text.Json?
UPDATE
.NET 6 added JsonSerializer.Serialize overloads that write to a stream. It's now possible to write just :
JsonSerializer.Serialize(stream,myObject);
This produces unindented JSON using UTF8 without BOM
Original Answer
It's unclear what the problem is, or what documentation and examples are missing, as there's are multiple sections in learn.microsoft.com and hundreds of blog posts and articles. In the docs JSON serialization and deserialization is a good place to start and How to serialize and deserialize (marshal and unmarshal) JSON in .NET includes the section Serialize to UTF8.
A MemoryStream is just a Stream wrapper over a byte[] array anyway, so serializing to a MemoryStream is the same as serializing to a byte[] array directly. This can be done with JsonSerializer.SerializeToUtf8Bytes:
byte[] jsonUtf8Bytes =JsonSerializer.SerializeToUtf8Bytes(weatherForecast);
And finally, in .NET anything that needs to serialize to something, works through Reader and Writer objects, like TextReader, StreamReader, TextReader and -Writers. In JSON.NET's case, this is done through the Utf8JsonWriter object. JsonSerializer.Serialize has an overload that writes to a Utf8JsonWriter :
using var stream=File.OpenWrite(somePath);
using var writer=new Utf8JsonWriter(stream);
JsonSerializer.Serialize(writer,myObject);
That's the slow way of using System.Text.Json though. Using buffers means allocating them and cleaning them up, which is costly, especially in web applications. For this reason, ASP.NET Core uses IO pipelines instead of streams to receive and send data to sockets, using reusable buffers leased from buffer pools and passed along each step in the ASP.NET Core pipeline. Passing byte[] buffers around copies their contents, so .NET Core introduced the Span<> and Memory<> types, which represent a view over an existing (possibly pooled) buffer. This way, ASP.NET Core passes those "views" of the buffers around, not the buffers themselves.
System.Text.Json was built to use pipelines and reusable memory instead of streams, allowing ASP.NET Core to use minimal memory and as few allocations as possible in high traffic web sites. ASP.NET Core uses the Utf8JsonWriter(IBufferWriter) constructor to write to the output pipeline through a PipeWriter.
We can use the same overload to write to a reusable buffer with an ArrayBufferWriter. That's the equivalent of using a MemoryStream BUT the output is accessed through either a ReadOnlySpan<byte> or Memory<byte> so it doesn't have to be copied around :
using var buffer=new ArrayBufferWriter<byte>(65536);
using var writer=new Utf8JsonWriter(buffer);
JsonSerializer.Serialize(writer,myObject);
ReadOnlySpan<byte> data=buffer.WrittenSpan;
The better option is to use newtonsoft.json.
It has lot of examples

request and response handling in asp.net through JSON

We are planning to start a new project in which this request and response scenario involved.
our client system may give request to create an order.If all the conditions are met our server system will generate an order and those details will get stored in our database.
server request is through asp.net JSON. JSON is totally new to me.Can any one suggest links through which i can learn and proceed with my project.
Regards,
Sathya
JSON is just an object, or array of objects, or list of objects, or... etc, serialized into a string.
The easiest way to understand it (and to put it in place), is using Json.Net:
http://james.newtonking.com/pages/json-net.aspx
It's really easy.
To understand JSON
JSON and ASP.NET
JSON Objects
MSDN Introduction to JSON
Serialize and Deserialize JSON Data
You would get many examples of it on internet.. Above are some which I used and were very helpful to me..

Pluggable, Extendable and RESTFUL Service Repository in ODATA or ASP.NET

I am going to develop a service repository using ODATA. As a result, I can model those public method of those classes as Entity Type with these properties automatically by reflection
Id. GUID
Output. It should depend on the type of the method return
... (Any other input parameters)
On the other hand, I will also expose these public methods as an Entity Set under the previous defined Entity Type. Finally, public user can invoke the expose services RESTFULLY by POSTing a JSON object to the specific the URI and retrieve OUTPUT from the return JSON object with the help of ODATA protocol
Unfortunately, either Array of Primary Object or Array of Complex Object are not supported in Entity Type definition. As a result, I can't define a SMTP Send Entity Type which will have multiple TO (string[]) or CC (also string[]) How can I deal with this problem?
I heard that Microsoft have announced ODATA V3.0 and ASP.NET Web API. Do these new technology can help? Or any other suggestion for implementation an extendable RESTFUL (or HTTP friendly since I would like to call it in javascript in html, php or any other web pages directly)? Thanks.
The OData V3 does support properties which are a collection of primitive or complex values. The WCF Data Services V5 implements this for custom and reflection providers (no support for EF providers yet).
See for example http://www.odata.org/media/30002/OData%20Atom%20Format.html#collectionofsimpletypedproperties
The latest WCF Data Services can be downloaded here: http://www.nuget.org/packages/Microsoft.Data.Services

Mocking HttpRequest in ASP.NET 4.0

I've seen a lot of similar threads but none that actually address my particular situation.
I'm writing unit tests in ASP.NET 4.0 web application (ASP.NET Forms, not MVC). There are several spots in the code where I call the ServerVariables collection to call variables like REMOTE_ADDR. Since my unit tests do not actually initiate HttpRequests when executing my code, things like ServerVariables are Null and therefore error when I try to call HttpContext.Current.Request.ServerVariables("REMOTE_ADDR")
All the solutions I've found to address this issue refer to MVC and so they assume that HttpRequest derives from HttpRequestBase, which it does in MVC but not in ASP.NET Forms.
I tried using Moq but you can't mock a sealed class, and HttpRequest is unfortunately sealed with no interface.
The HttpRequestBase and HttpRequestWrapper classes can be used with a bit of work.
Wherever you currently access HttpContext.Current.Request -- or just plain Page.Request -- you'll need to use an injectable instance of HttpRequestBase instead. Then you'll need to inject a different subclass of HttpRequestBase depending on whether you're testing or live.
For live code, you'd probably inject an HttpRequestWrapper instance that wraps HttpContext.Current.Request:
var liveRequest = new HttpRequestWrapper(HttpContext.Current.Request);
For test code, you'd need to create and inject your own mock subclass of HttpRequestBase. Presumably Moq can do that for you on-the-fly.

How to expose custom object (with sub objects) in web service?

I'm kinda new to web services and want to make sure I am doing things correctly.
I have a custom object which has sub objects as well. (let's say Company object, sub object is collection of Employee objects)
I want the web service to return a collection of Company objects. Do I make the service return a Dataset and custom generate a dataset with datatables representing the different objects?
What is the best way to do this? I tried to just serialize it, but that doesn't seem to work either.
I tried this dll
http://www.codeproject.com/KB/linq/linqsqlserialization.aspx
But the output XML doesn't seem to include the sub object.
Whether you're using the 2.0 framework (with ASMX web services, which are no longer supported) or the 3.0 framework (with WCF), both will handle return of complex objects provided they are serializable. In the 2.0 framework, that means capable of marking your objects with the [Serializable] attribute. In the 3.0 framework, you're implementing serialization using the [DataContract] attribute. http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx and http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx.
Both frameworks will enable the client-side WSDL in preparation for clients to consume your complex objects. Since they're non-primitives, you'll be limited to SOAP-based clients because the return payload will require complex representation.

Resources