I have a class hierarchy that I want to serialize to json in a tagged union in an array.
class BaseComponent
{
public string Id { get; set; }
}
class Child1: BaseComponent
{
public string Child1Prop { get; set; }
}
class Child2: BaseComponent
{
public string Child2Prop { get; set; }
}
class Wrapper
{
public List<BaseComponent> Components { get; set; }
}
Sample json
{
"components": [
{
"id": "id-1",
"type": "Child1",
"child2Prop": "Hello"
},
{
"id": "id-2",
"type": "Child2",
"child2Prop": "world"
},
]
}
The hierarchy is likely to change, and I want to publish a nice Json Schema for validation and manual editing of the json files in VSCode. Is there any reasonable tools that lets me generate this? Or even just some strongly typed json schema C# classes that i can use to create the schema from reflection.
My Nuget package JsonSchema.Net.Generation is built for generating schemas from C# code.
The docs and a playground can be found on https://json-everything.net/json-schema
Related
This is the JSON im receiving, already filtered. (its coming from the google places autocomplete API)
{
"predictions": [
{
"description": "Frankfurt am Main, Deutschland",
"place_id": "ChIJxZZwR28JvUcRAMawKVBDIgQ",
},
{
"description": "Frankfurt (Oder), Deutschland",
"place_id": "ChIJb_u1AiqYB0cRwDteW0YgIQQ",
},
{
"description": "Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"place_id": "ChIJX3W0JgQYvkcRWBxGlm6csj0",
}
],
"status": "OK"
}
And I need to get this JSON into this format:
{
"success":true,
"message":"OK",
"data":[
{
"description":"Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"id":"ChIJX3W0JgQYvkcRWBxGlm6csj0"
},
{
"description":"Frankfurt Airport (FRA), Frankfurt am Main, Deutschland",
"id":"ChIJeflCVHQLvUcRMfP4IU3YdIo"
},
{
"description":"Frankfurt Marriott Hotel, Hamburger Allee, Frankfurt am Main, Deutschland",
"id":"ChIJdag3xFsJvUcRZtfKqZkzBAM"
}
]
}
I would be very g
So predictions is just renamed to "data", we change rename status to message, move it up and add a success if the http-request that happened earlier was a success or not. This does not seem so hard on the first catch, but I can't seem to find resources to transform or rearrange JSON in C#.
I would be very grateful for any tips or resources, so I can get unstuck on this probably not so difficult task. I should mention I'm fairly new to all of this.
Thank you all in advance!
First create classes thats represent your jsons
public class Prediction
{
public string description { get; set; }
public string place_id { get; set; }
}
public class InputJsonObj
{
public Prediction[] predictions { get; set; }
public string status { get; set; }
}
public class Datum
{
public string description { get; set; }
public string id { get; set; }
}
public class OutPutJsoObj
{
public bool success { get; set; }
public string message { get; set; }
public List<Datum> data { get; set; }
public OutPutJsoObj(){
data = new List<Datum>();
}
}
Then mapped objects (manually or using any of mapping libraries like AutoMapper) and create final json.
using Newtonsoft.Json;
InputJsonObj inputObj = JsonConvert.DeserializeObject<InputJsonObj >(inputJson);
OutPutJsoObj outObj = new OutPutJsoObj ();
foreach(var p in inputObj)
{
outObj.Data.Add(new Datum() { descriptions = p.descriptions , id= p.place_id }
}
string outJson = = JsonConvert.SerializeObject(outObj);
Just parse the origional json and move the data to the new json object
var origJsonObj = JObject.Parse(json);
var fixedJsonObj = new JObject {
new JProperty("success",true),
new JProperty("message",origJsonObj["status"]),
new JProperty("data",origJsonObj["predictions"])
};
it is not clear from your question what should be a success value, but I guess maybe you need this line too
if (fixedJsonObj["message"].ToString() != "OK") fixedJsonObj["success"] = false;
if you just need a fixed json
json = fixedJsonObj.ToString();
or you can create c# class (Data for example) and deserilize
Data result= fixedJsonObj.ToObject<Data>();
I like the answer from #Serge but if you're looking for a strongly typed approach we can model the input and output structure as the same set of classes and the output structure is similar, with the same relationships but only different or additional names this try this:
The process used here is described in this post but effectively we create write-only properties that will receive the data during the deserialization process and will format it into the properties that are expected in the output.
public class ResponseWrapper
{
[JsonProperty("success")]
public bool Success { get;set; }
[JsonProperty("message")]
public string Message { get;set; }
[Obsolete("This field should not be used anymore, please use Message instead")]
public string Status
{
get { return null; }
set
{
Message = value;
Success = value.Equals("OK", StringComparison.OrdinalIgnoreCase);
}
}
[JsonProperty("data")]
public Prediction[] Data { get;set; }
[Obsolete("This field should not be used anymore, please use Data instead")]
public Prediction[] Predictions
{
get { return null; }
set { Data = value; }
}
}
public class Prediction
{
public string description { get; set; }
public string place_id { get; set; }
}
Then you can deserialize and re-serialize with this code:
using Newtonsoft.Json;
...
var input = JsonConvert.DeserializeObject<ResponseWrapper>(input);
var output = JsonConvert.SerializeObject(objs, new JsonSerializerSettings
{
Formatting = Newtonsoft.Json.Formatting.Indented,
NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore
});
This is a fiddle you can test with: https://dotnetfiddle.net/DsI5Yc
And the output:
{
"success": true,
"message": "OK",
"data": [
{
"description": "Frankfurt am Main, Deutschland",
"place_id": "ChIJxZZwR28JvUcRAMawKVBDIgQ"
},
{
"description": "Frankfurt (Oder), Deutschland",
"place_id": "ChIJb_u1AiqYB0cRwDteW0YgIQQ"
},
{
"description": "Frankfurt Hahn Flughafen (HHN), Lautzenhausen, Deutschland",
"place_id": "ChIJX3W0JgQYvkcRWBxGlm6csj0"
}
]
}
If you were going to go to the trouble of writing a converter for the deserialization then I find this solution is a bit simpler. I tend to use this type of solution when exposing additional properties to allow legacy data to map into a the current code base.
keeps the mapping and logic contained within the class
tells developers still writing code against the deprecated structures about the change
You can also augment this and implement a global converter to omit obsolete properties which would give you full backwards compatibility until you update the source to stop sending the legacy structure. This is a fiddle of such a solution: https://dotnetfiddle.net/MYXtGT
Inspired by these posts:
JSON.Net Ignore Property during deserialization
Is there a way to make JavaScriptSerializer ignore properties of a certain generic type?
Exclude property from serialization via custom attribute (json.net)
Json.NET: Conditional Property Serialization
I need to write a dynamically typed system in a statically typed language using the System.Text.Json lib in .net core 3.1. What I need is to deserialize files into a Python like dict. I have to use C# for this because we can't get python to authenticate against our systems correctly. And C# is our default lang, so...
In the real world, these documents are in CosmosDB. This program takes in a configuration file that points to specific fields in document structures and it has to inspect those fields, maybe update them, then write the result back to the CosmosDB. Currently, I'm building a system that points to the specific field. Since the system has no clue what the structure of the doc is that it's getting back from the cosmos, it can't use formal models. I am currently working on the system that inspects or updates the fields using static files in the test suite. I'll worry about cosmos DB when this is finished.
Take this file for example:
{
"azureSql": {
"databaseName": "ordersdb",
"tables": [
{
"tableName": "mytable",
"columnNames": [
"column1",
"column2"
]
}
]
},
"cosmosDb": {
"databaseName": "CosmosDb",
"collections": [
{
"collectionName": "TestCollection",
"fieldNames": [
"MyStatus.json.path.to.field"
]
}
]
}
}
files like this get read in. The input can be virtually any type of legit json. I need to put that into a Dictionary that I can access similar to Python mydict["cosmosDb"]["collections"][0]["fieldNames"]
The line:
_entities = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);
doesn't work because it only serializes the first level. The rest are json entry types.
I found this C# way to mimic Python Dictionary Syntax
and its close, but it only supports string and doesn't work in the Deserialize method.
Any ideas on how to solve this?
I suggest you to use dynamic
var entities = JsonSerializer.Deserialize<dynamic>(jsonString)
Would the following classes structure work?
public class Rootobject
{
public Azuresql azureSql { get; set; }
public Cosmosdb cosmosDb { get; set; }
}
public class Azuresql
{
public string databaseName { get; set; }
public List<Table> tables { get; set; }
}
public class Table
{
public string tableName { get; set; }
public List<string> columnNames { get; set; }
}
public class Cosmosdb
{
public string databaseName { get; set; }
public List<Collection> collections { get; set; }
}
public class Collection
{
public string collectionName { get; set; }
public List<string> fieldNames { get; set; }
}
Edit:
This works:
using Newtonsoft.Json.Linq;
var result = JObject.Parse(jsonFile);
var detail = result["cosmosDb"]["collections"][0]["collectionName"];
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have JSON in this format:
{
"user": "123#abc",
"filter": [
{
"filterName": "Filter1",
"condition": "1",
"values": [
"123"
]
},
{
"filterName": "Filter2",
"condition": "2",
"values": [
"ABC",
"XYZ"
]
}
],
"onlyMerge": "true",
"mergeBy": [
]
}
And I am using these classes
public class Outermost{
public string user;
public Root filter ;
public string onlyMerge;
public string mergeby;
}
public class values {
public string value { get; set; }
}
public class Filters {
public string filtername {get; set; }
public string condition {get; set;}
public values values { get; set; }
}
public class Root {
public List<Filters> Filters { get; set; }
}
JSONConvert.Deserialize(Outermost)
I have to deserialize the structure
Paste your JSON into http://json2csharp.com/ and you will see that your C# classes don't match the JSON.
Update 2:
Visual Studio 2013 has a built-in Json to C# Class converter tool! :)
Update:
Just a note about the great tool http://json2csharp.com/ : When working with a object that has properties of complex types, You may want to check about the classes it creates, because sometimes it'll created unnecessarily/undesirable classes. Example:
Json
var objJson = {
"id_product": 19,
"description": "Laptop",
"_links": {
"buy": {
"href": "/Product/Buy/19",
"title": "Buy It Now!"
},
"more_details": {
"href": "/Product/Details/19",
"title": "More Details..."
}
}
};
Generated Class/Classes:
public class Buy
{
public string href { get; set; }
public string title { get; set; }
}
public class MoreDetails
{
public string href { get; set; }
public string title { get; set; }
}
public class Links
{
public Buy buy { get; set; }
public MoreDetails more_details { get; set; }
}
public class RootObject
{
public int id_product { get; set; }
public string description { get; set; }
public Links _links { get; set; }
}
As You can see, the two classes Buy and MoreDetails have exactly the same structure and purpose, so You may want to replace it with a more generic class instead of using repeatedly classes (there are scenarios where this redundant structure is more appropriate). In a very similar scenario, I've created a class named Link.
Original Answer:
You don't said enough to be sure what's your problem. Try in the future specify better what are your difficulties and needs.
But, I guess your problem is some exception being throw or some properties not being bind...
If you pay attention, in your JSON example object, filter is directly a collection, and not a property that has a collection inside. Thus, just change
public Root filter; to public List<Filters> filter { get; set; }.
Also, mergeBy and values are collections, and not simple strings. You could use http://json2csharp.com/ to generate automatically the correspondent C# class of your JSON object, and check what properties are not matching... (Or, substitute your whole class, that is what I would recommend...)
This post (https://stackoverflow.com/a/14486260/894358) suggests that Breeze does support inheritance, but my breeze manager is not able to understand the metadata being returned from the API. The server will return metadata and JSON results from other action methods, but when the manager tries to interpret the metadata it throws the error: "Cannot read propertyRef of undefined".
Here is my very simple implementation (Code First Migrations generates the database):
namespace VerySimpleVehicleModel.Models
{
public abstract class Vehicle
{
public int Id { get; set; }
public int Speed { get; set; }
}
public class Bus: Vehicle
{
public int Capacity { get; set; }
}
public class Car : Vehicle
{
public string Color { get; set; }
}
public class VehicleContext : DbContext
{
public DbSet<Car> Cars { get; set; }
public DbSet<Bus> Buses { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
}
}
namespace VerySimpleVehicleModel.Controllers
{
[BreezeController]
public class BreezeController : ApiController
{
readonly EFContextProvider<VehicleContext> _contextProvider = new EFContextProvider<VehicleContext>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[HttpGet]
public IQueryable<Car> Cars()
{
return _contextProvider.Context.Cars;
}
}
}
And here is the metadata returned from the Metadata action method:
"{
\"schema\":
{
\"namespace\":\"VerySimpleVehicleModel.Models\",
\"alias\":\"Self\",
\"d4p1:UseStrongSpatialTypes\":\"false\",
\"xmlns:d4p1\":\"http://schemas.microsoft.com/ado/2009/02/edm/annotation\",
\"xmlns\":\"http://schemas.microsoft.com/ado/2009/11/edm\",
\"cSpaceOSpaceMapping\":\"
[
[
\\\"VerySimpleVehicleModel.Models.Vehicle\\\",
\\\"VerySimpleVehicleModel.Models.Vehicle\\\"
],
[
\\\"VerySimpleVehicleModel.Models.Car\\\",
\\\"VerySimpleVehicleModel.Models.Car\\\"
],
[
\\\"VerySimpleVehicleModel.Models.Bus\\\",
\\\"VerySimpleVehicleModel.Models.Bus\\\"
]
]
\",\"entityType\":
[
{
\"name\":\"Car\",
\"baseType\":\"Self.Vehicle\",
\"property\":
{
\"name\":\"Color\",
\"type\":\"Edm.String\",
\"fixedLength\":\"false\",\"maxLength\":\"Max\",
\"unicode\":\"true\",\"nullable\":\"true\"
}
},
{
\"name\":\"Bus\",
\"baseType\":\"Self.Vehicle\",
\"property\":{\"name\":\"Capacity\",
\"type\":\"Edm.Int32\",\"nullable\":\"false\"}
},
{
\"name\":\"Vehicle\",
\"abstract\":\"true\",
\"key\":{\"propertyRef\":{\"name\":\"Id\"}},
\"property\":
[
{\"name\":\"Id\",\"type\":\"Edm.Int32\",\"nullable\":\"false\",\"d4p1:StoreGeneratedPattern\":\"Identity\"},
{\"name\":\"Speed\",\"type\":\"Edm.Int32\",\"nullable\":\"false\"}
]
}
]
,\"entityContainer\":
{
\"name\":\"VehicleContext\",
\"entitySet\":
{
\"name\":\"Vehicles\",
\"entityType\":\"Self.Vehicle\"
}
}
}
}"
I can't reproduce a problem with your example. In fact, it works like a charm.
I copied it almost verbatim to the DocCode sample because it is so simple and we needed a simple one before getting into the complexities of the BillingDetails example (will publish next release).
This is a standard TPH model with an abstract base class. The key is recognized in metadata; look at the Vehicle base class for this line:
\"key\":{\"propertyRef\":{\"name\":\"Id\"}},
What is odd is that you are receiving metadata in the old string format. When I look at the payload for the metadata in my test runs I get a JSON object:
{"schema":{"namespace":"Inher ...
"entityType":[{"name":"Vehicle",
"abstract":"true",
"key":{"propertyRef":{"name":"Id"}}, ...
}]}]}}}
I suspect that you have an old copy of the Breeze.WebApi.dll. Perhaps you updated the Breeze NuGet package and the old assembly got trapped in the references or an old script file was retained. Try uninstalling and re-installing Breeze. Look carefully at the NuGet console log to make sure everything was replaced.
I've been trying to figure out why some of my tests haven't been working (TDD) and managed to track it down to serialization of a class, but I'm not sure why it's not working. There are two flavours, a simple version and a more complex version, the slightly more complicated one involves having an array of values within the Parameter.Value.
The simple version, I've got a class that can be serailzied using the JavaScriptSerializer (I'm assuming this is how MVC works when it generates JSON). The structure it produces looks like this:
{
"Name": "TestQuery",
"QueryId": 1,
"Parameters": [
{
"Name": "MyString",
"DataType": 0,
"Value": "A String",
"IsArray": false
}],
"Sql": "SELECT * FROM Queries"
}
There are 3 C# classes Query, ParameterCollection (which is a KeyedCollection<String, Parameter>) and a Parameter. All of these are marked up with DataContract/DataMember attributes and serialize via the DataContractSerializer without any problem.
The JavaScriptSerializer however, serializes the object correctly to the JSON above, but upon deserialization I have no Parameters, they just seem to get missed off.
Does anyone have any idea why these fails, and what I might be able to do to fix it?
Why KeyedCollection<String, Parameter>? You have an array, not dictionary, so your JSON should match the following structure:
public class Query
{
public int QueryId { get; set; }
public string Name { get; set; }
public string Sql { get; set; }
public Parameter[] Parameters { get; set; }
}
public class Parameter
{
public string Name { get; set; }
public int DataType { get; set; }
public string Value { get; set; }
public bool IsArray { get; set; }
}
and then you will be able to deserialize it without any problems:
var serializer = new JavaScriptSerializer();
var json = #"
{
""Name"": ""TestQuery"",
""QueryId"": 1,
""Parameters"": [
{
""Name"": ""MyString"",
""DataType"": 0,
""Value"": ""A String"",
""IsArray"": false
}],
""Sql"": ""SELECT * FROM Queries""
}";
var query = serializer.Deserialize<Query>(json);
Also you can get rid of [Data*] attributes from your view models, they are not used by the JavaScriptSerializer class.