RavenDB 4.0 storing raw json - c#

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; }
}

Related

check for duplicate properties in JSON

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

Alternatives to Dynamic JsonParsing

I am using aws lambda's api request template to create and call post methods. I have to send a post body from my code, which should be json serialized.
Here is a way I am doing it now:
**dynamic pObj = new JObject();
pObj.Url = DownloadURL;
pObj.Name = IName;
pObj.ID = I.ID;**
Custom redirect = new Custom()
{
url = new Uri(lambdaEndpoint),
**Body = pObj.ToString(),**
Method = "POST",
Available = true
};
But I read an article where it talks about performance issues with using dynamic keyword.
Is there an alternative approach of doing this that is better in performance? Any help will be appreciated.
Thanks
An alternative to the usage of dynamic would be a direct deserialization. Since you know your object, it should fit as expected.
Demo
using System;
using Newtonsoft.Json;
public class Program
{
// create you class. You can generate it with http://json2csharp.com/ or use Visual Studio (edit>past special)
public class Custom
{
public string Url { get; set; }
public string Name { get; set; }
public string Id { get; set; }
}
public void Main()
{
string json = "{ \"Url\":\"xyz.com\", \"Name\":\"abc\", \"ID\":\"123\" }";
// the magic code is here! Go to http://www.newtonsoft.com/json for more information
Custom custom = JsonConvert.DeserializeObject<Custom>(json);
// custom is fill with your json!
Console.WriteLine(custom.Name);
}
}
output:
abc

How can I loop through the JSON on Universal Windows Platform with C#

I am developing an app on UWP.
When I connect with a server api and I get the next response I don't have problems.
{"value":"Login successfull","sessionId":"a95077855b05ed0fec5d7fa3abafa126e15aba2a"}
I can get information in the following way:
JsonObject jsonObject = JsonObject.Parse(jsonString);
string token = jsonObject["sessionId"].GetString();
string value = jsonObject["value"].GetString();
but my problem is when i get the next response of the api:
[{"person":{"name":"name1","country":"Spain","city":"user_city","phone":null}},{"person":{"name":"name2","country":"Turkey","city":"user_city","phone":"1111111"}},{"person":{"name":"name3","country":"Argentina","city":"user_city","phone":"22222"}},{"person":{"name":"name4","country":"Argentina","city":"user_city","phone":"33333"}}]
How can I loop through the JSON and get all the people that match a condition?
I have to do with "Windows.Data.Json"
If interested in a solution using only Windows.Data.Json namespace, here it is:
var rootValue = JsonValue.Parse(jsonString);
foreach (var item in rootValue.GetArray())
{
var unamedObject = item.GetObject();
var personObject = unamedObject["person"].GetObject();
System.Diagnostics.Debug.WriteLine(personObject["name"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["country"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["city"].GetString());
System.Diagnostics.Debug.WriteLine(personObject["phone"].GetString());
}
Why would somebody pick Windows.Data.Json over Newtonsoft's Json.net?
If your JSON needs are simple, you can reduce the size of your app ~1 MB by choosing Windows.Data.Json because it is part of the operating system.
I would recommend you try out Json.net nuget package and deserialise the json payload to classes through that.
A good tutorial can be found here: http://windowsapptutorials.com/windows-phone/general/deserialize-json-data-using-newtonsoft-json-net-library/
But if you search you'll find more.
In short, you first copy paste your json and use Visual Studio > File > Paste Special > To paste to classes ( first open an empty cs file and set your cursor inside it ).
After that you use JsonConvert.DeserializeObject<RootObject>() to actually parse the json string.
Once parsed you'll have an array of items if your original json also defined an array.
Note RootObject is the first class object in the generated classes in Visual Studio
There are ways to do it without external libraries, if that is the real reason for the stipulation of Windows.Data.Json.
I'd likely do it something like this...
First I'd make some classes representing the returning JSON:
public class RootObject
{
public Person person { get; set; }
}
public class Person
{
public string name { get; set; }
public string country { get; set; }
public string city { get; set; }
public string phone { get; set; }
}
Then add a little method to deserialize:
public static T Deserialize<T>(string json)
{
var bytes = Encoding.Unicode.GetBytes(json);
using (var ms = new MemoryStream(bytes))
{
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
And finally deserialize and query that result like so:
var persons = Json.Deserialize<List<RootObject>>(textBox.Text);
var peeps = (from p in persons
where p.person.name.StartsWith("name")
select p).ToList();

Can the C# MongoClient be used to return valid JSON without first serializing to a .NET type?

I would like to have ASP.NET MVC return a document stored in MongoDB as JSON, but have no need for it to be serialized to a .NET type first. However, BSONDocument.ToJSON() returns JSON that looks like this:
{_id:ObjectId("someid")}
The browser's JSON parser does not like "ObjectId(nnn)" and so the call fails with a parser error. I am able to get parse-able JSON using a Regex hack:
public ActionResult GetFormDefinitionsJSON()
{
var client = new MongoDB.Driver.MongoClient(ConfigurationManager.ConnectionStrings["mongodb"].ConnectionString);
var db = client.GetServer().GetDatabase("formthing");
var result = db.GetCollection("formdefinitions").FindAll().ToArray();
var sb = new StringBuilder();
sb.Append("[");
var regex = new Regex(#"(ObjectId\()(.*)(\))");
var all = result.Select(x => regex.Replace(x.ToJson(), "$2"));
sb.Append(string.Join(",", all));
sb.Append("]");
return Content(sb.ToString(), "application/json");
}
This returns parse-able JSON:
{_id:"someid"}
But it smells. Is there any way without regex and string building hackery to get the official MongoDB driver to return JSON that can be parsed by the browser? Alternatively, am I missing something on the browser side that would allow {_id:ObjectId("someid")} to be parsed as valid?
You have two options that I can think of.
The first one is to use the JavaScript JsonOutputMode mode. This results in the ID serializing to "_id" : { "$oid" : "51cc69b31ad71706e4c9c14c" } - not quite ideal, but at least it's valid Javascript Json.
result.ToJson(new JsonWriterSettings { OutputMode = JsonOutputMode.JavaScript })
The other option is to serialize your results into an object and use the [BsonRepresentation(BsonType.String)] attribute. This results in much nicer Json: "_id" : "51cc6a361ad7172f60143d97"; however, it requires you to define a class to serialize it in to (this can affect performance)
class Example
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public ObjectId ID { get; set; }
public string EmailAddress { get; set; }
}
// Elsewhere in Code - nb you need to use the GetCollection<T> method so
// that your result gets serialized
var result = database.GetCollection<Example>("users").FindAll().ToArray();
var json = result.ToJson();
Some more details on the differences between JsonOuputModes (Strict, Javascrpt and Mongo):
http://docs.mongodb.org/manual/reference/mongodb-extended-json/

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