check for duplicate properties in JSON - c#

We are working on a .Net core based web api application and for this we have a requirement to validate incoming request body which is JSON format against the c# based type.
We are at this point evaluating NJsonSchema library to see if it throws duplicate property error.
But looks like it doesnt support this validation. We also checked JSON schema validator from NewtonSoft but seems like it doesnt support duplicate property validations either.
Below is the minimized code using NJsonSchema that we use -
using NewtonSoft.Json;
public class MyRequest
{
[JsonRequired]
[JsonProperty("name")]
public string Name { get; set; }
}
and when we pass a JSON object like this -
{"name":"abc","name":"xyz"}
We need our JSON validator to throw error for duplicate property
Our example test looks like this -
[Test]
public async System.Threading.Tasks.Task SchemaValidation_WithDuplicateProperty_Async()
{
var jsonString = await File.ReadAllTextAsync("Data//JsonWithDuplicateProperty.json");
var schema = JsonSchema.FromType<MyRequest>();
var errors = schema.Validate(jsonString);
Assert.That(errors.Count(), Is.EqualTo(1));
}
So my question - Has anyone done this in the past? Or are there any libraries for .net core that provides JSON validation for duplicate properties and/or can this be done using NJsonSchema or NewtonSoft.

As #zaggler notes, using Newtonsoft, you can use the DuplicatePropertyNameHandling enum. Unfortunately, you can't use it directly in a a call to DeserializeObject (in the JsonSerializerSettings); it has to be used in a JToken Reader. See this discussion thread for more details:
https://github.com/JamesNK/Newtonsoft.Json/issues/931
Here is a method that wraps the action up in a DeserializeObject-esque way:
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IO;
public class Program
{
public static void Main()
{
var json = #"{""name"":""abc"",""name"":""xyz""}";
var objA = DeserializeObject<MyRequest>(json, new JsonSerializerSettings(), DuplicatePropertyNameHandling.Ignore);
Console.WriteLine(".Ignore: " + objA.Name);
var objB = DeserializeObject<MyRequest>(json, new JsonSerializerSettings(), DuplicatePropertyNameHandling.Replace);
Console.WriteLine(".Replace: " + objB.Name);
var objC = DeserializeObject<MyRequest>(json, new JsonSerializerSettings(), DuplicatePropertyNameHandling.Error);
Console.WriteLine(".Error: " + objC.Name); // throws error before getting here
}
public static T DeserializeObject<T>(string json, JsonSerializerSettings settings, DuplicatePropertyNameHandling duplicateHandling)
{
JsonSerializer jsonSerializer = JsonSerializer.CreateDefault(settings);
using (var stringReader = new StringReader(json))
using (var jsonTextReader = new JsonTextReader(stringReader))
{
jsonTextReader.DateParseHandling = DateParseHandling.None;
JsonLoadSettings loadSettings = new JsonLoadSettings {
DuplicatePropertyNameHandling = duplicateHandling
};
var jtoken = JToken.ReadFrom(jsonTextReader, loadSettings);
return jtoken.ToObject<T>(jsonSerializer);
}
}
}
public class MyRequest
{
[JsonRequired]
[JsonProperty("name")]
public string Name { get; set; }
}
Output:
.Ignore: abc
.Replace: xyz
Run-time exception (line 31): Property with
the name 'name' already exists in the current JSON object. Path
'name', line 1, position 21.
See:
https://dotnetfiddle.net/EfpzZu

Related

How to retreive a field from JSON string stored in database

I have the following string
[{"id":"select","component":"select","editable":true,"index":2,
"label":"WHAT IS THIS QUESTION?","description":"some description",
"placeholder":"placeholder","options":["Aligator","Pikachi"],"required":false,
"validation":".*","$$hashKey":"object:38"}]
stored in the database.
I want to retrieve the validation value. How can I achieve this using LINQ? I am using EF Core 2 and I am unable to get ahead in my query.
var jsonData = from table in myRepository.Table
where table.JSONString.........
JSONString is the name of the column which stores the JSONString.
I have been searching posts on SO and on google for a while but have not found an answer yet. Thanks.
Updates:
I created classes for the JSON like the following
public class FormJson
{
[JsonProperty(PropertyName = "id")]
public string id { get; set; }
[JsonProperty(PropertyName = "component")]
public string component { get; set; }
...
}
but I still dont have a clue on how I can map this specific class to my JSON Object and get the field I need.
Download the NuGet-package Newtonsoft.Json to include using Newtonsoft.Json.Linq;
Fetch the JSON-string from your database:
var jsonStr = (from t in myRepository.Table select t.JSONString).FirstOrDefault();
Parse the JSON-string to JObject:
JObject json = JObject.Parse(jsonStr);
Read the value of a specific token you want to access:
JToken validation = json.GetValue("validation");
Do something with the value:
string value = validation.ToString();
JToken[] valueArray = validation.ToArray();
Edit:
As there is something wrong with JObject.Parse() whilst using your JSON (which is valid), instead of using JObject.Parse(), try the following:
// Add references for TextReader and JsonReader
using Newtonsoft.Json;
using System.IO;
TextReader tReader = new StringReader(jsonStr);
JsonReader jReader = new JsonTextReader(tReader);
JObject json = JObject.Load(jReader);
JToken validation = json.GetValue("validation");

RavenDB 4.0 storing raw json

I'm using NLOG for logging with the JsonLayout and want to store these logs in RavenDB 4.0. Basically I'm trying to store raw json into ravendb using the RavenDB .NET client.
I had a working solution in RavenDB 3.5: using a class inheriting from "RavenJObject" but this functionality was depricated in 4.0.
I got it working by deserializing the json into a dynamic and storing the dynamic object. But this stores the document in a meaningless "JObjects" collection. It also seems overhead to convert json to dynamic and back to json again.
var obj = JsonConvert.DeserializeObject<dynamic>(jsonString);
session.Store(obj);
I know it's possible using the UI (raven studio). But i can't find a way doing it using the .Net client.
I think it's maybe possible using the http api, doing a post with the raw json. But I haven't found any documentation about this.
Who can help me out? Any advice is appreciated!
Here you have code sample which allows you to operate on generic type and assigns collection properly:
[Fact]
public void RawJson()
{
using (var store = GetDocumentStore())
{
using (var session = store.OpenSession())
{
var json = "{ 'Name' : 'John', '#metadata' : { '#collection': 'Users' } }";
var blittableJson = ParseJson(session.Advanced.Context, json);
var command = new PutDocumentCommand("users/1", null, blittableJson);
session.Advanced.RequestExecutor.Execute(command, session.Advanced.Context);
}
using (var session = store.OpenSession())
{
var user = session.Load<User>("users/1");
Assert.Equal("John", user.Name);
}
}
}
public BlittableJsonReaderObject ParseJson(JsonOperationContext context, string json)
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
return context.ReadForMemory(stream, "json");
}
}
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}

Json.NET StringEnumConverter not working as expected

I'm attempting to use Json.NET with the System.Net.Http.HttpClient to send an object with an enum property, however the enum is always serialized as an integer value rather than the string equivalent.
I've tried following the instructions here:
http://james.newtonking.com/archive/2013/05/08/json-net-5-0-release-5-defaultsettings-and-extension-data
By both adding an instance of StringEnumConverter to the JsonSerializerSettings and also tried to decorate the enum property with [JsonProperty(ItemConverterType = typeof(StringEnumConverter))] neither of which appear to be working in my example.
I'm using Json.NET version 5.0.8
Can anyone tell me what I'm doing wrong please? Here is a sample console app to replicate showing both the global serializer settings and the decorated property:
Thanks.
using System;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
HttpClient client = new HttpClient { BaseAddress = new Uri("http://test-uri.com") };
JsonConvert.DefaultSettings = (() =>
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter());
return settings;
});
var data = new TestData { Enum = TestEnum.Hello };
// The following posts: {"Enum":0}
// Shouldn't it post {"Enum":"Hello"} instead?
var result = client.PostAsJsonAsync("/test", data).Result;
}
public class TestData
{
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
public TestEnum Enum { get; set; }
}
public enum TestEnum
{
Hello,
World
}
}
}
I've inspected this with Fiddler and it posts: {"Enum":0} rather than {"Enum":"Hello"} which is what I would expect.
The ItemConverterType property of the JsonPropertyAttribute attribute is the converter to use for items of a collection. You should be using the JsonConverterAttribute attribute.
public class TestData
{
[JsonConverter(typeof(StringEnumConverter))]
public TestEnum Enum { get; set; }
}
I think I've found a way of getting it to work without decorating attributes. It involves replacing client.PostAsJsonAsync() with client.PostAsync(). You can then specify the MediaTypeFormatter to use which in this case will be the JsonMediaTypeFormatter.
Source here: .net HttpClient with custom JsonConverter
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter());
var formatter = new JsonMediaTypeFormatter { SerializerSettings = settings };
var response = client.PostAsync("/test", data, formatter).Result;
This still doesn't explain why the DefaultSettings aren't being applied. I can only assume that the presence of the [JsonConverter] property forces the HttpClient to use Json.NET for serialization, otherwise it just uses the default serializer.

Loading a .json file into c# program

I am trying to move my website from XML based config files to JSON based ones. Is there a way to load in a .json file in so that it turns into the object? I have been searching the web and I cannot find one. I already have the .xml file converted and saved as a .json. I would rather not use a 3rd party library.
You really should use an established library, such as Newtonsoft.Json (which even Microsoft uses for frameworks such as MVC and WebAPI), or .NET's built-in JavascriptSerializer.
Here's a sample of reading JSON using Newtonsoft.Json:
JObject o1 = JObject.Parse(File.ReadAllText(#"c:\videogames.json"));
// read JSON directly from a file
using (StreamReader file = File.OpenText(#"c:\videogames.json"))
using (JsonTextReader reader = new JsonTextReader(file))
{
JObject o2 = (JObject) JToken.ReadFrom(reader);
}
Another good way to serialize json into c# is below:
RootObject ro = new RootObject();
try
{
StreamReader sr = new StreamReader(FileLoc);
string jsonString = sr.ReadToEnd();
JavaScriptSerializer ser = new JavaScriptSerializer();
ro = ser.Deserialize<RootObject>(jsonString);
}
you need to add a reference to system.web.extensions in .net 4.0 this is in program files (x86) > reference assemblies> framework> system.web.extensions.dll and you need to be sure you're using just regular 4.0 framework not 4.0 client
As mentioned in the other answer I would recommend using json.NET. You can download the package using NuGet. Then to deserialize your json files into C# objects you can do something like;
JsonSerializer serializer = new JsonSerializer();
MyObject obj = serializer.Deserialize<MyObject>(File.ReadAllText(#".\path\to\json\config\file.json");
The above code assumes that you have something like
public class MyObject
{
public string prop1 { get; set; };
public string prop2 { get; set; };
}
And your json looks like;
{
"prop1":"value1",
"prop2":"value2"
}
I prefer using the generic deserialize method which will deserialize json into an object assuming that you provide it with a type who's definition matches the json's. If there are discrepancies between the two it could throw, or not set values, or just ignore things in the json, depends on what the problem is. If the json definition exactly matches the C# types definition then it just works.
Use Server.MapPath to get the actual path of the JSON file and load and read the file using StreamReader
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class RootObject
{
public string url_short { get; set; }
public string url_long { get; set; }
public int type { get; set; }
}
public class Program
{
static public void Main()
{
using (StreamReader r = new StreamReader(Server.MapPath("~/test.json")))
{
string json = r.ReadToEnd();
List<RootObject> ro = JsonConvert.DeserializeObject<List<RootObject>>(json);
}
Console.WriteLine(ro[0].url_short);
}
}
Note : Look below link also I have answered for question similar to this.It will be help full for you
How to Parse an example string in C#
I have done it like:
using (StreamReader sr = File.OpenText(jsonFilePath))
{
var myObject = JsonConvert.DeserializeObject<List<YourObject>>(sr.ReadToEnd());
}
also, you can do this with async call like: sr.ReadToEndAsync().
using Newtonsoft.Json as reference.
Hope, this helps.
See Microsofts JavaScriptSerializer
The JavaScriptSerializer class is used internally by the asynchronous
communication layer to serialize and deserialize the data that is
passed between the browser and the Web server. You cannot access that
instance of the serializer. However, this class exposes a public API.
Therefore, you can use the class when you want to work with JavaScript
Object Notation (JSON) in managed code.
Namespace: System.Web.Script.Serialization
Assembly: System.Web.Extensions (in System.Web.Extensions.dll)

NancyFx binding a model to a dynamic type?

In Nancy, is there a way to bind the content of a POST request to a dynamic type?
For example:.
// sample POST data: { "Name": "TestName", "Value": "TestValue" }
// model class
public class MyClass {
public string Name { get; set; }
public string Value { get; set; }
}
// NancyFx POST url
Post["/apiurl"] = p => {
// this binding works just fine
var stronglyTypedModel = this.Bind<MyClass>();
// the following bindings do not work
// there are no 'Name' or 'Value' properties on the resulting object
dynamic dynamicModel1 = this.Bind();
var dynamicModel2 = this.Bind<dynamic>();
ExpandoObject dynamicModel3 = this.Bind();
var dynamicModel4 = this.Bind<ExpandoObject>();
}
Out of the box Nancy does not support dynamic model binding. TheCodeJunkie has written a quick ModelBinder to achieve that tho.
https://gist.github.com/thecodejunkie/5521941
Then you can use it like so
dynamic model = this.Bind<DynamicDictionary>();
As the previous answers point, there is no support for binding directly to a dynamic type, the most similar one is the ModelBinder provided by TheCodeJunkie in https://gist.github.com/thecodejunkie/5521941
However, this approach has a problem, and is that the DynamicDictionary resulting from this code does not serialize later properly, producing only the keys of the dictionary and losing the values. This is described here Why does storing a Nancy.DynamicDictionary in RavenDB only save the property-names and not the property-values? and as of today (version 1.4.3) is still happening, limiting seriously this approach.
The solution is to use a simple trick, accessing the raw data received in the POST and deserializing using JSON.Net. In your example it would be:
using System;
using System.Dynamic;
using Nancy;
using Nancy.Extensions;
using Newtonsoft.Json;
Post["/apiurl"] = p => {
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(Request.Body.AsString());
//Now you can access the object using its properties
return Response.AsJson((object)new { a = obj.Prop1 });
}
Note that you need to use Nancy.Extensions for the Request.Body.AsString() call.
I was looking for a way to deserialize my POST body as dynamic and found this question, I’ll put my solution using Newtonsoft and extension method just in case that result useful for somebody else.
Extension method
using System.IO;
using Nancy;
using Newtonsoft.Json;
namespace NancyFx
{
public static class DynamicModelBinder
{
public static dynamic ToDynamic(this NancyContext context)
{
var serializer = new JsonSerializer();
using (var sr = new StreamReader(context.Request.Body))
{
using (var jsonTextReader = new JsonTextReader(sr))
{
return serializer.Deserialize(jsonTextReader);
}
}
}
}
}
Usage
using Nancy;
using Nancy.ModelBinding;
namespace NancyFx
{
public class HomeModule : NancyModule
{
public HomeModule(IAppConfiguration appConfig)
{
Post("/product", args => {
dynamic product = Context.ToDynamic();
string name = product.Name;
decimal price = product.Price;
return Response.AsJson(new {IsValid=true, Message= "Product added sucessfully", Data = new {name, price} });
});
}
}
}
I'm not sure, but you can try:
dynamic model = new ExpandoObject();
model = Request; //or Request.Form
return View["ViewName", model];
let me know if works :)

Categories

Resources