I am having difficulty writing the appropriate annotations to represent data which is returned from a JSON Get request which returns data like so:
[{"ProductCode":"0129923083091","Description":"DIESEL ","SalesLitres":6058.7347,"SalesValue":6416.2000},{"ProductCode":"0134039344902","Description":"UNLEADED ","SalesLitres":3489.8111,"SalesValue":3695.7100},
...
]
(ellipsis above just indicating that I could have variable number of these items returned)
In my model class (I am using MVVM approach for a Xamarin project but that's not over relevant here) I am using annotations to represent the model attributes
namespace App8.Models
{
public class ReportRow
{
[JsonProperty("ProductCode")]
public string ProductCode { get; set; } = string.Empty;
[JsonProperty("Description")]
public string Description { get; set; } = string.Empty;
[JsonProperty("SalesLitres")]
public double SalesLitres { get; set; } = 0.0;
[JsonProperty("SalesValue")]
public double SalesValue { get; set; } = 0.0;
}
}
I would like to annote another class which shows the container/contained relationship. However, I coming unstuck as there is no JSON attribute to provide in the annotation to represent the "root" of the returned collection.
I'd have no problem mapping the JSON to an object model for any JSON arrays which are named within the returned JSON. In that case I could create another class with a named JSON attribute which contained a C# List but I am trying to provide an appropriate model mapping for JSON which returns a list of items within an unnamed array.
Any ideas how I could approach this?
To deserialize that JSON, use:
JsonConvert.DeserializeObject<List<ReportRow>>(json)
(or any variant you wish, the key here is asking to deserialize a ICollection of ReportRow. It could be your own class implementing ICollection, or any of the builtins)
The same idea follows to JsonTextReader or whatever other means of deserializing JSON.NET offers. Just use a ICollection<YourType> as target type.
Related
Situation
I'm writing an application that can display different types of charts, with a vanilla JS front-end and an ASP.NET 6 API back-end. There are different types of charts in the application, like LineChart and PieChart. These different chart types all implement the interface IChart. The IChart interface has properties for information like the ID and name of the chart, whereas the LineChart, for example, has properties for the name of the X- and Y-axis.
Here are the model classes as minimum example:
IChart.cs:
public interface IChart
{
public int Id { set; get; }
public string Title { set; get; }
public string Description { set; get; }
}
LineChart.cs:
public class LineChart : IChart
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string xAxisName { get; set; }
public string yAxisName { get; set; }
}
My Problem
When I'm returning a single chart using an ASP.NET API-controller, it works just fine; if the object I return is a LineChart, I get the property xAxisName in the JSON response even if the API method return type is IChart. But when I return multiple Charts in a List<IChart>, I only get the properties provided by IChart in my JSON, so no xAxisName. How can I make a controller return all the items in the List<IChart while keeping the properties of the derived classes in the JSON result? Is there some JSON middleware setting I need to set?
As an illustration, here are a few sample API calls.
The following code returns a LineChart with the X axis name set in the JSON reponse body:
// POST: api/<ChartController>
[HttpPost("{xAxisName}")]
public IChart MakeOneChart(string xAxisName)
{
IChart chart = new LineChart() {xAxisName = xAxisName};
return chart;
}
But in the following code, the response body is not filled with values for the X axis
// GET: api/<ChartController>
[HttpPost]
public IEnumerable<IChart> MakeManyCharts()
{
IEnumerable<IChart> charts = new List<IChart>()
{
new LineChart() {xAxisName = "Duration (in minutes)"},
new LineChart() {xAxisName = "Duration (in years)"}
};
return charts;
}
People here had a similar problem. The returned object has an interface as a property and the derived classes didn't serialize properly but I bet any property defined in that interface would show.
Now I don't know much about the inner workings of Json conversion but from what I've observed, here's what I'm guessing is happening:
The standard Json serializer provided with .Net doesn't recognize the object's runtime type so it doesn't know anything about the object beneath the surface. Only exception is the outermost object you're returning. For example, when your return type is IChart, it can recognize when you return LineChart or PieChart since it's just checking the outermost object. However, when you return IEnumerable<IChart> it can't recognize the derived type of IChart objects since they're wrapped by IEnumerable. I bet if you returned List<IChart> it could recognize properties related to List<T>
There are some possible solutions:
An answer in the linked question suggests using Json.NET package from Newtonsoft
A different answer from the question suggests writing custom serializers
Another option I would say would be to try writing a custom formatter. Check the type of object and cast it like this:
if (context.Object is IEnumerable<LineChart> lineCharts)
{
// serialize the lineCharts
}
else if (context.Object is IEnumerable<PieChart> pieCharts)
{
// serialize the pieCharts
}
// return the json string
though this type of solution probably would not work if you have base interfaces as properties like in the question and you wanted the derived objects of those to be recognized in the serialization (or rather you would have to write nested if statements and check every property of every chart object in the IEnumerable and it would go on and on if you have a very complex object), but should be enough for your situation
I am deserializing a json file into C# objects, in this case MTG cards (json from scryfall.com). Previously I solved the problem with MSSQL; I imported the json data into the database with OPENJSON mapping each element I needed to a column with a proper name, and then created a new json file with properties that matched the class I needed in the application.
For properties like arena_id that have only one value, or for arrays that were used as strings (color_identity), it was not an issue, but for arrays such as card_faces (cards that have 2 playable sides) I had to specify which element of the array I wanted to import.
arena_id int '$.arena_id',
...
[color_identity] nvarchar(max) AS JSON,
...
name_front varchar(100) '$.card_faces[0].name',
mana_cost_front varchar(30) '$.card_faces[0].mana_cost',
name_back varchar(100) '$.card_faces[1].name',
mana_cost_back varchar(30) '$.card_faces[1].mana_cost',
From the SQL tailored file I deserialized every object accordingly, but now I'm trying to do it without SQL and with more direct influence as to what goes where.
As mentioned before, for properties that do not need to be deconstructed I know I can just pick the same property name in the class and it gets accepted easily. My problem is with array properties that I should divide into separate properties with different names.
Is it possible in C# to deserialize in a similar way?
I'm thankful for all kinds of hints and tips as I am relatively new to programming with only one year in a training course.
You just need to create a class that properly represents the expected JSON format, EG:
public class MyClass
{
public int Arena_Id { get; set; }
public CardFace[] Card_Faces { get; set; }
}
public class CardFace
{
public string Name { get; set; }
public string Mana_Cost { get; set; }
}
Then deserialize your JSON:
string json = ... // your data
MyClass x = new Newtonsoft.Json.JsonSerializer() // or whichever serializer you want
.DeSerialize<MyClass>(json)
With this deserialized instance of your class, you can then translate it to another class matching the structure you require.
I am guessing that you are simply able to at least call the JSON.
C# does have a good JSON deserialization directive.
// keep the class as close to how it will show up on the JSON
public class MTGCard
{
public string name;
public int manaCost;
...
}
// then you should be able to just
currentCard = JsonSerializer.Deserialize<MTGCard>(jsonString);
The class and the json are close enough you should be able to simply use the Array features pull the cards you need and then just deserialize it on the call based
I have the following JSON definitions:
public class Msg
{
[JsonProperty(PropertyName = "commandId")]
public int CommandId { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "params")]
public JnoParams Parameters = new JnoParams();
}
public class Res_Msg
{
[JsonProperty(PropertyName = "commandId")]
public int CommandId { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
[JsonProperty(PropertyName = "params")]
public Dev_Result Parameters = new Dev_Result();
}
The property "params" has different JSON definitions, in the first one it is an empty structure, in the second it contains fields with values returned by the device.
When the WebSocket returns a packet to the app, I determine what to do based on the two fields "commandId" and "id" and am trying to unwrap the "params" with a different structure definition depending on the values in commandId and id.
If I had to use plain old 'c' language I would use a union, but I'm using JSON definitions.
How can I tell C# that "params" is of one type rather than another and therefore access its fields accordingly?
I thought I could convert from type1 to type2 or somehow cast it, but so far have been unsuccessful.
Thank you much for any hint.
Well;
apparently I didn't have to define every possible JSON structure in order to retrieve the values from them.
I defined the JSON for those structures I was going to use and within these structures, I only defined the fields I am interested in.
After decades spent describing structures that had to be defined exactly as the various fields in terms of type and size, it is a refreshing feeling to see that all I need really is to define the type of JSON packet and the name and type of field contained in it that I want to extract, in order for JSON to find it and return it, without any need to define in the JSON structure all the fields before it or after.
I have an object, Project, that contains many fields, some complex some not. It is an EF class, so I can't edit it to add attributes.
I just want to generate a JSON object containing 2 of the fields (one int (id) and one string (name))
I'd hate to create another ViewModel just for this...
In my viewmodel I have a List<Project>. Is there a way to use HTML helpers to get a JSON representation of only the properties I choose without using attributes?
Here is an example of the Project class:
public class Project
{
public int Id {get; set; } <-- Serialize this
public string Name { get; set; } <-- Serialize this
public Object AnotherObject [ Get; Set; } <-- Ignore this
....
}
I'd like it to become:
[{"id":"27","name":"test1"},{"id":"34","name":"test2"},{"id":"35","name":"test3"}]
The ultimate goal here to is output the json directly to the view as a var so that it can be used in building a JsGrid.
If there is a way to do it with Html helpers, that would be great.
Thanks!
Json.NET has a great built in ignore feature. If you tag the Property you want to exclude with the [JsonIgnore] attribute, the serializer will not serialize that property.
[JsonIgnore]
public bool IsValid { get; set; }
I'm trying to deserialize Google's Translate API JSON response into an C# object using JavaScriptSerializer. However, it always says Type 'TranslateAPI.Models.Translations' is not supported for deserialization of an array.. I double checked whether I have correctly created models for this object and it seems right. Here are my models:
TranslateResult
public TranslateData data { get; set; }
TranslateData
public Translations translations { get; set; }
Translations
public TranslatedText[] translatedText { get; set; } // I have also tried List<TranslatedText> which also doesn't work
TranslatedText
public string translatedText { get; set; }
The json returned from Google looks like this:
{data: {
translations: [
{translatedText: "Hello world"}
]
}
Any idea what I'm doing wrong?
Thanks
PS. It could be useful to mention, that I'm deserializing it like so: TranslateResult translateResult = js.Deserialize <TranslateResult>(json);
I suspect you don't need the Translations class at all. You've got:
An object containing a data property
The data property value is an object containing a translations property
The translations property value is an array
Each element of the array is an object with a translatedText property
So that sounds to me like your TranslateData class should be:
TranslateData
public Translation[] translations { get; set; }
Translation // Renamed from TranslatedText
public string translatedText { get; set }
(I'd also recommend that you rename the properties to follow normal C# naming conventions, and then apply attributes to help with the JSON conversion if you need to. I haven't used JavaScriptSerializer for a while, but I'm sure it's feasible. You shouldn't need to work with nasty property names in your C# code.)