Newtonsoft Jsons Deserialize doesn't parse $ref objects - json

I know it should work but it doesn't and i have no idea what the heck is wrong with that.
The Root["parameters"]-Dictionary is properly deserialized but the List of Parameter-References in root["paths"]["/alliances/"]["get"]["parameters"] is a bulk of null's.
I've shortened the Content. Origninal is here.
Switching from (my own) SwaggerParameter.Deserializable (which works fine in Root) to JSchema just changes from producing null's to throwing exception "Could not resolve schema reference '#/parameters/datasource'."
[TestClass()]
public class RootNode_Tests
{
static string s_sContent = #"{
""parameters"":{
""X-User-Agent"":{
""description"":""Client identifier, takes precedence over User-Agent"",
""in"":""header"",
""name"":""X-User-Agent"",
""type"":""string""
},
""datasource"":{
""default"":""tranquility"",
""description"":""The server name you would like data from"",
""enum"":[
""tranquility"",
""singularity""
],
""in"":""query"",
""name"":""datasource"",
""type"":""string""
}
},
""paths"":{
""/alliances/"":{
""get"":{
""operationId"":""get_alliances"",
""parameters"":[
{
""$ref"":""#/parameters/datasource""
},
{
""$ref"":""#/parameters/X-User-Agent""
}
]}
}
}
}";
[TestMethod]
public void Deserialize_Test()
{
TestRoot tr = JsonConvert.DeserializeObject<TestRoot>(s_sContent, new JsonSerializerSettings()
{
MetadataPropertyHandling = MetadataPropertyHandling.ReadAhead, // shouldnt be neccessary
Formatting = Formatting.Indented,
PreserveReferencesHandling = PreserveReferencesHandling.All,
TraceWriter = new DbgPrintTracer()
});
}
public class DbgPrintTracer : ITraceWriter
{
public TraceLevel LevelFilter => TraceLevel.Verbose;
public void Trace(TraceLevel level, string message, Exception ex) => Debug.Print($"{level}: {message} ({ex})");
}
public abstract class _TestBase
{
[JsonExtensionData]
public Dictionary<string, object> m_dictAdditional { get; protected set; }
}
public class TestRoot : _TestBase
{
[JsonProperty("parameters")]
//[JsonConverter(typeof(ParamCollectionConverter))]
//public Dictionary<string,SwaggerParameter.Deserializable> m_dictParameters { get; protected set; } /* DESERIALIZES PROPERLY */
public Dictionary<string, JSchema> m_dictParameters { get; protected set; }/* DESERIALIZES PROPERLY TOO */
[JsonProperty("paths")]
public Dictionary<string, Paths> m_dictPaths { get; protected set; }
}
public class Paths : Dictionary<string, Method>
{
}
public class Method : _TestBase
{
[JsonProperty("parameters")]
// [JsonConverter(typeof(ParamCollectionConverter))]
// public List<SwaggerParameter.Deserializable> m_lstParams { get; protected set; } /* ONLY NULL's, NO EXEPTIONS */
public List<JSchema> m_lstParams { get; protected set; } /* EXCEPTION: Couldnt resolve reference-Path. */
}
}

Related

Using JsonUtility FromJson to deserialize JSON in Unity

I'm having an issue while trying to read a Json string in unity.
I created Classes based on the json response im receiving
but im not able to deserialize this json
Where I did wrong, can anybody help?
{
"status": 200,
"isSuccess": true,
"message": "Suggestion Found",
"response": {
"result": [
{
"OriginalWord": "goodboy",
"suggests": [
{
"suggestWords": "good boy"
},
{
"suggestWords": "Cordoba"
},
{
"suggestWords": "Catawba"
},
{
"suggestWords": "Catawba's"
}
]
}
]
}
}
My Classes
[Serializable]
public class Suggest
{
[SerializeField]
public string suggestWords { get; set; }
}
[Serializable]
public class Result
{
[SerializeField]
public string OriginalWord { get; set; }
[SerializeField]
public List<Suggest> suggests { get; set; }
}
[Serializable]
public class Response
{
[SerializeField]
public int status { get; set; }
[SerializeField]
public bool isSuccess { get; set; }
[SerializeField]
public string message { get; set; }
[SerializeField]
public List<Result> result { get; set; }
}
Im Deserializing like this
Response response = JsonUtility.FromJson<Response>(jsonString);
Above every class there got to be [System.Serializable] this is because UnityEngine has its own implementation of Serializable so you got to indicate its the System's that you want to use rather then Unity's. You also dont need to have [SerializeField] since this is if you want to show the property in Unity's inspection window and since this will not go onto any gameobject you dont need this. You just got to make it public.
Also in the class public class Response if you want json to map correctly you wouldnt use public List<Result> result { get; set; }, it would have to be named response and it will have to be 1 object not a list. So you can create a class called Results and have it have a list variable called result and it will be a list of type Result (no s). and in the result it would have the OriginalWord and a list of Suggest called suggests
Moreover, you must have a constructor for each class for it to work. So it would look like this:
[System.Serializable]
public class Suggest
{
public string suggestWords;
public Suggest(string suggestWords)
{
this.suggestWords = suggestWords;
}
}
[System.Serializable]
public class Result
{
public string OriginalWord;
public List<Suggest> suggests;
public Result(string OriginalWord, List<Suggest> suggests)
{
this.OriginalWord = OriginalWord;
this.suggests = suggests;
}
}
[System.Serializable]
public class Results
{
public List<Result> result;
public Results(List<Result> result)
{
this.result = result;
}
}
[System.Serializable]
public class Response
{
public int status;
public bool isSuccess;
public string message;
public Results response;
public Response (int status, bool isSuccess, string message, Result response)
{
this.status = status;
this.isSuccess = isSuccess;
this.message = message;
this.response = response;
}
}

EF Core and Pomelo 5.0 - how to query the json column

I'm using last version of json implementation in Pomelo 5.0 and configure my maria server to use microsoft json serialisation.
var serverVersion = new MariaDbServerVersion(new Version(10, 3, 0));
services.AddDbContext<BusinessManagementDbContext>(options =>
{
options.UseMySql(databaseConfiguration.ConnectionString, serverVersion, m =>
{
m.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically);
m.EnableRetryOnFailure();
});
options.EnableSensitiveDataLogging(true);
});
I can save my POCO in my db but when I try to query my data, I get a null object.
Here's my data :
HeidySQL data
My query is pretty simple but I think I'm not using the right way for json query.
await Context.ValidatedSaleInvoices.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
It seems like there is no deserialization between my data and my property "Content".
How can I do this ?
Thank you,
Edit
My model is :
public class ValidateSaleInvoiceEntity
{
public int Id { get; set; }
public ValidateSaleInvoiceContent Content { get; set; }
}
public class ValidateSaleInvoiceContent
{
public string BusinessName { get; set; }
public DateTime Date { get; internal set; }
public string Number { get; internal set; }
public Address Address { get; internal set; }
public List<ValidateSaleInvoiceLineEntity> Lines { get; internal set; } = new List<ValidateSaleInvoiceLineEntity>();
}
public class ValidateSaleInvoiceLineEntity
{
public string Description { get; internal set; }
public decimal Quantity { get; internal set; }
public decimal UnitPriceVatExcluded { get; internal set; }
public decimal VatRate { get; internal set; }
}
And my json Result was like this (empty, like there waere no deserialisation: { "BusinessName":"", "Date":"", "Number":"" etc.}
My boss stop my poc about MariaDB Json implementation so I had to go back to this good old friend pure sql column :/ . That's why I haven"t a full json result. Sorry
For a property to serialize/deserialize JSON automatically to a POCO, you need to tell Pomelo, that the table column of the property is of the MySQL/MariaDB type json:
public class ValidateSaleInvoiceEntity
{
public int Id { get; set; }
[Column(TypeName = "json")] // <-- this is one way to do it
public ValidateSaleInvoiceContent Content { get; set; }
}
public class MyContext : DbContext
{
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ValidateSaleInvoiceEntity>()
.Property(e => e.Content)
.HasColumnType("json"); // <-- this is another way to do it
}
}
Here is a fully working console project:
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
// EF Core entities:
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
// Either use this data annotation, or the corresponding Fluent API call (see
// OnModelCreating), to explicitly mark the column type as JSON.
[Column(TypeName = "json")]
public IceCreamDetails Details { get; set; }
}
// JSON class:
public class IceCreamDetails
{
public int Kilojoule { get; set; }
public int Rating { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var connectionString = "server=127.0.0.1;port=3306;user=root;password=;database=So68020732";
var serverVersion = ServerVersion.AutoDetect(connectionString);
optionsBuilder.UseMySql(connectionString, serverVersion, options => options
.UseMicrosoftJson(MySqlCommonJsonChangeTrackingOptions.FullHierarchyOptimizedSemantically))
.UseLoggerFactory(
LoggerFactory.Create(
configure => configure
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<IceCream>(
entity =>
{
// Either use this Fluent API call, or the corresponding data annotation
// (see the IceCreamDetails class), to explicitly mark the column type as JSON.
entity.Property(e => e.Details)
.HasColumnType("json");
entity.HasData(
new IceCream {IceCreamId = 1, Name = "Vanilla", Details = new IceCreamDetails { Kilojoule = 865, Rating = 9 }},
new IceCream {IceCreamId = 2, Name = "Chocolate", Details = new IceCreamDetails { Kilojoule = 903, Rating = 10 }});
});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var iceCreams = context.IceCreams
.OrderBy(i => i.IceCreamId)
.ToList();
Trace.Assert(iceCreams.Count == 2);
Trace.Assert(iceCreams[0].Details.Kilojoule == 865);
Trace.Assert(iceCreams[1].Details.Rating == 10);
}
}
}
You can find the most comprehensive JSON sample code on our repository (see the JSON mapping and query scenarios section).

How to use a POCO object to access an array of options in the appsettings.json file (ASP.NET 5)

I am using ASP.NET 5 and I want to use POCO classes to access my appsettings.json file. This file looks like this:
{
"Data": {
"ErpSystemConnection": {
"ConnectionString": "[myConnectionString]"
}
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Verbose",
"System": "Information",
"Microsoft": "Information"
}
},
"GoogleAnalytics": {
"Account": [
{
"Name": "AccountName",
"ServiceAccountEmailAddress": "someEmail#someaccount.iam.gserviceaccount.com",
"KeyFileName": "key1.p12",
"Password": "notasecret"
},
{
"Name": "AnotherAccount",
"ServiceAccountEmailAddress": "anotherEmailAccount#someotheraccount.iam.gserviceaccount.com",
"KeyFileName": "key2.p12",
"Password": "notasecret"
}
],
"KeyFilePath": "/googleApis/"
}
}
The 'GoogleAnalytics' key contains an array of accounts that I wish to be able to access in a collection either as a list or an array.
I created a POCO to represent this key that contains a corresponding collection of 'Account' objects:
public class GoogleAnalytics
{
public Account[] Account { get; set; } = new Account[1];
public string KeyFilePath { get; set; }
public GoogleAnalytics()
{
}
}
And the 'Account' object:
public class Account
{
private const string _applicationName = #"Storefront Analytics";
private X509Certificate2 _certificate;
private ServiceAccountCredential _credential;
private AnalyticsService _service;
#region |--Properties--|
public string Name { get; set; }
public string Password { get; set; }
public string ServiceAccountEmailAddress { get; set; }
public string KeyFileName { get; set; }
public string KeyFilePath { get; set; }
public string KeyFileFullPath
{
get
{
return $"{KeyFilePath}{KeyFileName}";
}
}
public X509Certificate2 Certificate
{
get
{
if(_certificate == null)
{
ConfigureInstance();
}
return _certificate;
}
set
{
_certificate = value;
}
}
public ServiceAccountCredential Credential
{
get
{
if (_credential == null)
{
ConfigureInstance();
}
return _credential;
}
set
{
_credential = value;
}
}
public AnalyticsService Service
{
get
{
if (_service == null)
{
ConfigureInstance();
}
return _service;
}
set
{
_service = value;
}
}
#endregion
#region |--Constructors--|
public Account()
{
}
public Account(string password, string keyFileName,
string keyFilePath,
string serviceAccountEmailAddress, string accountName)
{
//TODO: Validate parameters
Password = password;
KeyFileName = keyFileName;
KeyFilePath = keyFilePath;
ServiceAccountEmailAddress = serviceAccountEmailAddress;
Name = accountName;
}
#endregion
private void ConfigureInstance()
{
Certificate = new X509Certificate2(KeyFileFullPath, Password, X509KeyStorageFlags.Exportable);
Credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(ServiceAccountEmailAddress)
{
Scopes = new[] { AnalyticsService.Scope.Analytics }
});
Service = new AnalyticsService(new BaseClientService.Initializer()
{
HttpClientInitializer = Credential,
ApplicationName = _applicationName
});
}
}
My Controller:
public class GoogleAnalyticsController : Controller
{
#region |--Properties--|
[FromServices]
private IGoogleAnalyticsRepository _repo { get; set; }
#endregion
public GoogleAnalyticsController(IOptions<GoogleAnalytics> options)
{
var temp = options.Value;
}
}
The 'KeyFilePath' property is properly set in the IOptions instance.
The problem I am having is that the Account array contains null references - none of the accounts are being instantiated. I wondering if I am doing this wrong, or the Options Model doesn't support this type of behavior at this time?
Update in response to Shaun Luttin's answer
I implemented the changes listing in Shaun Luttin's answer. There seems to have been an additional problem. For whatever reason, all of the Account instances' properties were null until I simplified the class as follows:
public class Account
{
public string Name { get; set; }
public string Password { get; set; }
public string ServiceAccountEmailAddress { get; set; }
public string KeyFileName { get; set; }
public string KeyFilePath { get; set; }
}
Short Answer
I wondering if I am doing this wrong, or the Options Model doesn't support this type of behavior at this time?
You are doing one thing wrong. The Options Model does support arrays. You need NOT to initialize your array property with an array of size [1].
public Account[] Account { get; set; } = new Account[1]; // wrong
public Account[] Account { get; set; } // right
Demo
Here is a sample, just for you, that you can find here on GitHub.
MyOptions.cs
namespace OptionsExample
{
public class MyObj
{
public string Name { get; set; }
}
public class MyOptions
{
public string Option1 { get; set; }
public string[] Option2 { get; set; }
public MyObj[] MyObj { get; set; }
}
}
Startup.cs
namespace OptionsExample
{
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.OptionsModel;
using System.Linq;
public class Startup
{
public IConfigurationRoot Config { get; set; }
public Startup(IHostingEnvironment env)
{
Config = new ConfigurationBuilder().AddJsonFile("myoptions.json").Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<MyOptions>(Config);
}
public void Configure(IApplicationBuilder app,
IOptions<MyOptions> opts)
{
app.Run(async (context) =>
{
var message = string.Join(",", opts.Value.MyObj.Select(a => a.Name));
await context.Response.WriteAsync(message);
});
}
}
}
myoptions.json
{
"option1": "option1val",
"option2": [
"option2val1",
"option2val2",
"option2val3"
],
"MyObj": [
{
"Name": "MyObj1"
},
{
"Name": "MyObj2"
}
]
}
project.json dependencies
"dependencies": {
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final"
}
Output

Service Stack POST-Request Body-Format / Transformation

iam using a RequestClass with the Route anotation to call a Json-Client POST method.
Now, while the paramters are structured like this
public class GetTicketRequest: IReturn<JsonObject>
{
public string CartId {
get;
set;
}
public string PriceId {
get;
set;
}
}
The BackendAPI needs them to be nesten in "data" in the json request, so more like
{
"data":[
{"cartid":123,
"priceId":11}]
}
Is there any way to transfrom the request object for the body before calling
JsonServiceClient _restClient = new JsonServiceClient(baseUrl);
JsonObject oneResponse = _restClient.Post(options);
This solution is useful where many DTOs require to be wrapped & converted, and is highly reusable, with no changes to your existing DTOs.
You can convert the requests of the JsonServiceClient by overriding the methods that handle preparing the requests for sending. Which means implementing your own extended JsonServiceClient as given below.
If you want to do this for all verbs then you override it's Send<TResponse> methods (otherwise, if it's just for POST then uncomment the commented out code, and remove the Send methods).
public class MyJsonServiceClient : JsonServiceClient
{
public Dictionary<Type, Func<object, object>> DtoConverters = new Dictionary<Type, Func<object, object>>();
public MyJsonServiceClient() {}
public MyJsonServiceClient(string baseUri) : base(baseUri) {}
public MyJsonServiceClient(string syncReplyBaseUri, string asyncOneWayBaseUri) : base(syncReplyBaseUri, asyncOneWayBaseUri) {}
public override TResponse Send<TResponse>(object request)
{
return base.Send<TResponse>(ConvertRequest(request));
}
public override TResponse Send<TResponse>(string httpMethod, string relativeOrAbsoluteUrl, object request)
{
return base.Send<TResponse>(httpMethod, relativeOrAbsoluteUrl, ConvertRequest(request));
}
/*
public override TResponse Post<TResponse>(string relativeOrAbsoluteUrl, object requestDto)
{
return base.Post(relativeOrAbsoluteUrl, ConvertRequest(requestDto));
}
*/
object ConvertRequest(object request)
{
Type dtoType = request.GetType();
return (DtoConverters.ContainsKey(dtoType)) ? DtoConverters[dtoType](request) : request;
}
}
Usage:
So given this DTO:
[Route("/test", "POST")]
public class TicketRequest : IReturnVoid
{
public string CartId { get; set; }
public string PriceId { get; set; }
}
You simply add the converter:
var client = new MyJsonServiceClient("http://localhost:9000");
// Simple converter for TicketRequest
client.DtoConverters.Add(typeof(TicketRequest), dto => {
var d = (TicketRequest)dto;
return new {
data = new {
CartId = d.CartId.ToInt(),
PriceId = d.PriceId.ToInt()
}
};
});
client.Post(new TicketRequest { CartId = "123", PriceId = "456" });
i solved this issue using a typed data property
public class GetTicketRequest: IReturn<JsonObject>
{
public class TicketCreateData
{
public int priceId {
get;
set;
}
}
public string CartId {
get;
set;
}
public string PriceId {
get;
set;
}
public List<TicketCreateData> data {
get {
var list = new List<TicketCreateData>();
list.Add(new TicketCreateData {
priceId = this.PriceId.ToInt()
});
return list;
}
set {
data = value;
}
}
}
To notes on this:
if neede, use DataContract/DataMember(Name="") to rename fields or only do partial serializing
Do never use structs for, like in this case, the data class - they are not serializeable at all
in my spefici case data even needs to be an array, thats why i used the list

How to deserialize a child list of interfaces?

I'm using ASP.NET MVC2 and I have the following object structure:
public class IDealer {
string Name { get; set; }
List<IVehicle> Vehicles { get; set; }
}
public class DealerImpl {
public string Name { get; set; }
public List<IVehicle> Vehicles { get; set; }
}
public interface IVehicle {
string Type { get; }
}
public class Car : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
public class Truck : IVehicle {
public string Type { get { return this.GetType().FullName; } }
}
I have the following class as my ModelBinder which deserializes objects in my page requests:
public class JsonModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
return deserialize(controllerContext, bindingContext);
}
protected static bool IsJSONRequest(ControllerContext controllerContext) {
var contentType = controllerContext.HttpContext.Request.ContentType;
return contentType.Contains("application/json");
}
protected virtual object deserialize(ControllerContext controllerContext, ModelBindingContext bindingContext) {
Type modelType = bindingContext.ModelMetadata.ModelType;
bool isNotConcrete = bindingContext.ModelMetadata.ModelType.IsInterface || bindingContext.ModelMetadata.ModelType.IsAbstract;
if (!IsJSONRequest(controllerContext)) {
return base.BindModel(controllerContext, bindingContext);
} else {
var request = controllerContext.HttpContext.Request;
var jsonStringData = new StreamReader(request.InputStream).ReadToEnd();
if (isNotConcrete) {
Dictionary<string, Object> result = JsonConvert.DeserializeObject<Dictionary<string, Object>>(jsonStringData);
string type = result["Type"] as string;
modelType = Type.GetType(type + ",MyCompany.Common");
}
return JsonConvert.DeserializeObject(jsonStringData, modelType, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
}
}
}
// ASP.NET MVC Controller
protected override void Initialize(System.Web.Routing.RequestContext requestContext) {
base.Initialize(requestContext);
ModelBinders.Binders.DefaultBinder = new JsonModelBinder();
}
[HttpPost]
public ActionResult addUpdateDealer(IDealer dealer) {
// breaks before here with the error in the comment below
}
// and in the aspx page
<script>
var model = <%= JsonConvert.SerializeObject(Model, Formatting.None, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }) %>;
</script>
The problem I'm running into, is that when the code tries to deserialize the child list of IVehicles, it does not know which type of vehicle to instantiate. I put a property on IVehicle called "Type" which could be used to help determine which class to instantiate, but I'm not sure what/where/how to provide an override to perform this check.
Your solution is similar to what JSON.NET has built-in now, called TypeNameHandling. Here are the release notes on that.
Your JSON message will need to include a $type property, which won't be deserialized, but will be interpreted by the deserializer as the concrete type to use.