I am trying to retrieve JSON data from an API, but one of the property names comes as #data.context . It is not a nested property. I've tested it with ExpandoObject as well, it is exactly like that. Normally in C # I would make a data model like
public class Data
{
public string #odata.context { get ; set; }
}
But this doesn't work, as C# doesn't let me have a comma in the variable name, nor have quotes around it. The # sign is already there
The JSON is as follows: this property that contains a link and then another one that contains a list of objects.
{
"#odata.context": "some link here",
"list" [ {}, {}
]
}
The list of objects do not give me any trouble, only the first property.
You can use JsonPropertyName attribute to map the json to the property e.g:
[JsonPropertyName("#odata.context")]
public string DataContext { get ; set; }
Microsoft docs
You might be consuming a poorly designed API. There are API specifications that tells how to structure and name JSON keys.
One way you can accomplish is
1 Fetch JSON response from API
2 Replace "#data.context" with "Context" or something similar in JSON string
3 Create class with property
public class Data
{
public string Context { get ; set; }
}
4 Deserialise it
Related
C#
Let's say that I want to make a winform page that receives a certain amount of random features read from the contents of a json file. (while using the 'Newtonsoft.Json' library)
Code that Deserializes the json file
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(File.ReadAllText(#"..\..\Json\features.json"));
These classes are set to represent the contents and structure of the json file within the same c# file, but outside of the main class of course.
class Root
{
public List<Category> category { get; set; }
}
class Category
{
public string name { get; set; }
public List<Feature> feature { get; set; }
}
class Feature
{
public string name { get; set; }
public string frequency { get; set; }
}
I have followed the practices to make this happen here:
https://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm
Can not deserialize JSON with array in array
https://json2csharp.com/
It works, however there are two main problems I can see with this:
It can end up visually cluttering the .cs file with lots of classes that you'll never need to look or edit, especially when it is interpreting multiple json files
It isn't dynamic, and it will only work for one json file that you have to make compatible by creating these classes for every subclass that exists within the json file.
My question: Is there a way to deserialize the json file without having to resort to creating multiple classes for every sub-category of data in the json string?
I believe you don't need the classes, because you don't need the whole JSON string, is that correct?
If so, instead of deserializing the whole json file, you could partially deserialize only the parts which you are interested in.
Have a look at this example from the Newtonsoft.Json documentation, where we have a long json string representing a response from a Google search, but are only interested in the responseData/results part of it, and only in some fields of that result object:
Object to partially desierialize:
public class SearchResult
{
public string Title { get; set; }
public string Content { get; set; }
public string Url { get; set; }
}
Deserializing Partial JSON Fragment Example:
string googleSearchText = #"{
'responseData': {
'results': [
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'url': 'http://en.wikipedia.org/wiki/Paris_Hilton',
'visibleUrl': 'en.wikipedia.org',
'cacheUrl': 'http://www.google.com/search?q=cache:TwrPfhd22hYJ:en.wikipedia.org',
'title': '<b>Paris Hilton</b> - Wikipedia, the free encyclopedia',
'titleNoFormatting': 'Paris Hilton - Wikipedia, the free encyclopedia',
'content': '[1] In 2006, she released her debut album...'
},
{
'GsearchResultClass': 'GwebSearch',
'unescapedUrl': 'http://www.imdb.com/name/nm0385296/',
'url': 'http://www.imdb.com/name/nm0385296/',
'visibleUrl': 'www.imdb.com',
'cacheUrl': 'http://www.google.com/search?q=cache:1i34KkqnsooJ:www.imdb.com',
'title': '<b>Paris Hilton</b>',
'titleNoFormatting': 'Paris Hilton',
'content': 'Self: Zoolander. Socialite <b>Paris Hilton</b>...'
}
],
'cursor': {
'pages': [
{
'start': '0',
'label': 1
},
{
'start': '4',
'label': 2
},
{
'start': '8',
'label': 3
},
{
'start': '12',
'label': 4
}
],
'estimatedResultCount': '59600000',
'currentPageIndex': 0,
'moreResultsUrl': 'http://www.google.com/search?oe=utf8&ie=utf8...'
}
},
'responseDetails': null,
'responseStatus': 200
}";
// Parse JSON into a JObject, which we can easily traverse
JObject googleSearch = JObject.Parse(googleSearchText);
// get JSON result objects into a list
IList<JToken> results = googleSearch["responseData"]["results"].Children().ToList();
// serialize JSON results into .NET objects
IList<SearchResult> searchResults = new List<SearchResult>();
foreach (JToken result in results)
{
// JToken.ToObject is a helper method that uses JsonSerializer internally
SearchResult searchResult = result.ToObject<SearchResult>();
searchResults.Add(searchResult);
}
// Title = <b>Paris Hilton</b> - Wikipedia, the free encyclopedia
// Content = [1] In 2006, she released her debut album...
// Url = http://en.wikipedia.org/wiki/Paris_Hilton
// Title = <b>Paris Hilton</b>
// Content = Self: Zoolander. Socialite <b>Paris Hilton</b>...
// Url = http://www.imdb.com/name/nm0385296/
This way, we only need to create a class for SearchResult and for nothing else, which sounds like what you want to have. While traversing the JSON object with code like googleSearch["responseData"]["results"] you can check whether the result is null and act accordingly, which means you can have optional fields in your JSON file, which are not present in other files, without your code breaking.
Does this help you solve your issues?
I believe that best practice is using classes.
Here are my arguments for using classes in your case:
You can read json bit by bit, use JObject.Parse or even dynamic (ugh) but it think your code should depend on a class (a dto) not on a json string. This input structure happens to be stored in json, but may not be. Most of your unit tests should take in an object, not a string.
I find the argument of deserialising to classes not being dynamic weak because you need to write the code that will handle the added elements. In other words if you add a new feature to json it won't just work, you need to write the code to support it and each time you change the json structure you need to update your code anyway.
I'm following the Create a web API with ASP.NET Core and MongoDB Tutorial
And I'm missing something very basic, how to serialize a Json object that has a property that could be "multi-type". That is, this property could be a string, or another object.
This is the original Json sample in the tutorial:
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson"
}
This is the original POCO Model in the tutorial:
public class Book
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
[BsonElement("Name")]
public string BookName { get; set; }
public string Author { get; set; }
}
When I call the "Post" and "Get" action on the WebApi Controller, the original Json model is serialized correctly into an instance of Book, saved into the database and retrieved correctly from the web service as expected.
But, I need to add a "MultiUse" property, where MultiUse could either be a string or an object, or something else, for example:
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson",
"MultiUse": "Some String"
}
or
{
"_id" : ObjectId("5bfd996f7b8e48dc15ff215d"),
"Name" : "Design Patterns",
"Author" : "Ralph Johnson",
"MultiUse": {
"foo":"bar"
}
}
If I try to Post an object, this MultiUse property is serialized as another .Net type.
What type should MultiUse be in the POCO Model? I've tried Object, Dynamic, BsonDocument, but it is clear I think that AspNet and MongoDb driver will not automatically serialize it as I need it.
This multi-value property is pretty much the only reason I want to use MongoDB, I just want to save whatever is in that property into the database.
This situation works without problems in ExpressJS, but I need .Net because of client requirements.
Of course, I can just drop the strongly typed schema and use the MongoDB net driver functions to insert, update and query without the models. But since this "should" be a common scenario I just wanted to know how is this being handled.
There are several ways to solve this problem,
my suggestion is to import Newtonsoft.Json, then use MultiUse as JObject, or if MultiUse is array as Jarray.
I am calling an api where result is coming this way {{'orderNo':123456}}
the same I am trying to access through these codes lines and But it is not working:
using System.Text.Json.JsonDocument doc = System.Text.Json.JsonDocument.Parse(payment["notes"]);
What I feel it should be very easy to access. Well, I just don't want to create any DTO for the same.
FYI, I can achieve this using DTO class like this:
RzOrderDto orderNo = System.Text.Json.JsonSerializer.Deserialize<RzOrderDto>(payment["notes"].ToString(),null);
public class RzOrderDto {
public string orderno { get; set; }
}
Now, here I don't want to use any object class but just want value of orderNo
{{'orderNo':123456}} is not a valid Json document, you should try to change the data that is coming from the API. If you cannot do that, you need to get rid of the outer pair of braces before parsing the string into an object.
The following documentation illustrates how to use the Options Pattern in ASP.NET Core to create a strongly-typed options class to access JSON configuration data.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options
This C# class
public class MyOptions
{
public string Option1 { get; set; }
public int Option2 { get; set; }
}
represents a portion of this JSON configuration file (the first two root-level properties)
{
"option1": "value1_from_json",
"option2": -1,
"subOptions": {
"subOption1": "subvalue1_from_json",
"subOption2": 200
}
}
I want to add another C# property named SubOptions to the MyOptions class that returns the raw data of the subOptions JSON sub-section, without creating a strongly-typed class for that sub-section of the JSON configuration file, but I don't know what data type to use (or if it's even possible to do that).
If I use string, I get a runtime error when service.Configure<MyOptions>(Configuration); is called, saying System.InvalidOperationException: 'Cannot create instance of type 'System.String' because it is missing a public parameterless constructor.
If I use object or dynamic, I get a different runtime error when service.AddSingleton(cfg => cfg.GetService<IOptions<MyOptions>>().Value); is called to register an instance of the MyOptions class, saying System.ArgumentNullException: 'Value cannot be null. Parameter name: type'
If I use JObject, I get {} back when I access the SubOptions property of the MyOptions object that's injected into my API Controller.
I know I can convert the sub-section to a JSON string property by escaping the sub-section data, but I want to avoid treating the sub-section as a string, and instead leave it as raw JSON.
Is it possible to do what I want to do? Is there a data type that works with the Options Pattern that will allow me to access the JSON sub-section without having to create a strongly-typed class?
*For background, I'm trying to create an API Controller method that returns the content of the JSON sub-section to the API client. I want to avoid using a strongly-typed class for the sub-section, so that the JSON configuration file can be edited on the server, adding new properties and values to the sub-section that will be returned to the API client, without having to update the C# code and redeploy the API service. In other words, I want the JSON sub-section to be 'dynamic', and just pull it and send it to the client. *
You can sorta do get raw configuration object by forcing your SubOptions property to be of IConfigurationSection:
public class MyOptions
{
public string Option1 { get; set; }
public int Option2 { get; set; }
public IConfigurationSection SubOptions { get; set; } // returns the "raw" section now
public string SubOptions_take2 { get; set; }
}
so you would still bind your strongly typed object in your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyOptions>(Configuration);
...
}
but this is where luck appears to run out, because even though it is a whole section - as far as options binder is concerned it's all been deserialised and parsed into hierarchy of values already. There appears to be no easy way to reassemble it back into one string. Injecting IOptionsMonitor allows you to get the values by opting for .GetChildren() but I could not find an obvious way to get the whole hierarchy without writing custom code to just recursively walk it (which I will leave out for you to play with should you feel this is worth the effort):
public IndexModel(IOptionsMonitor<MyOptions> options)
{
_options = options.CurrentValue;
var subOptions = _options.SubOptions as ConfigurationSection;
var children = subOptions.GetChildren(); // you see, the config has already been parsed into this hierarchy of items - it's too late to get the raw string value
var s = JsonConvert.SerializeObject(children);
// will produce something like this JSON:
//[{"Path":"SubOptions:subOption1","Key":"subOption1","Value":"subvalue1_from_json"},{"Path":"SubOptions:subOption2","Key":"subOption2","Value":"200"}]
}
one way around it will be to actually encode your json as string in the config file:
"subOptions_take2": "{\"subOption1\": \"subvalue1_from_json\",\"subOption2\": 200}"
then you can just grab it later:
public IndexModel(IOptionsMonitor<MyOptions> options)
{
_options = options.CurrentValue;
var subOptions_string = _options.SubOptions_take2;// this is valid json now: {"subOption1": "subvalue1_from_json","subOption2": 200}
}
I guess, you can use JObject from Newtonsoft.Json package - it's the default JSON parser & serializer in Asp.Net Core
Ok, so I am using MVC 3 and it is great at de-serializing a JSON data set into a strongly typed object that is passed to my controller action. Unfortunately I have not found a solution to a more dynamic case.
Does the built in Json de-serialisation and classes have support for an "undefined" property set? For example lets say I have some fixed data like name and age, but I also want to pass down a dynamically created rating list where the user could enter (or select) a movie and set a rating value in a table.
The model data structure could be something like this:
public class UserRatings
{
public string Name { get; set; }
public int Age { get; set; }
public Dictionary<string,int> Ratings { get; set; }
}
But assuming that my Json dataset looks like this from javascript:
var data = { Name: name, Age: age, Ratings: rating };
Where the rating variable is a dynamically constructed object that consist of the name (or id) of of the movie as key and rating as number. Naturally de-serialization of this in the controller action will not be successful as it does not understand the mapping of Ratings to the rather complex dictionary object. But is there a generic Json collection that I could use instead for Ratings as an intermediate format?
I have tried making the Ratings object a Json string in javascript and just sending a string down, but I am unable to find a "factory" or something that can make a Json structure in C# that I can iterate over to get the data out. The classes Json and JsonResult does not help me in this regard it seems. Basically how can I use the built in Json support in MVC to do my own de-serialisation into some generic Json collection object?
You Could Use JavaScriptSerializer or DataContractSerializer with Some ActionFilters. They're very flexible
See my answer Passing dynamic json object to C# MVC controller basically using a dynamic type and a ValueProviderFactory is the cleanest way to deserialize Json to something more dynamic.
There's another option which i prefer using for being neater...(we eliminate the step of getting data from the request stream)
Here's a code sample
Cat catObj = new Cat();
if (TryUpdateModel<Cat>(catObj))
{
//do stuff
}
else
{
//invalid input
}
The TryUpdateModel resides in the controller namespace and hence no need to add any additional reference.
If you just need the Json sent in as part of the request you could obtain it using the following block of code(you could also obtain it from Request.Form)
using (StreamReader reader = new StreamReader(Request.InputStream))
{
var inputJson = reader.ReadToEnd();
}