Can dapper deserialize json stored as text? - c#

public class MyType
{
public int Id { get; set;}
public int[] MyArray { get; set; }
}
var sql = "SELECT id, MyArrayAsJson as MyArray";
var x = await connection.QueryAsync<MyType>(sql);
I have a string stored in the database which looks like json: [1,2,3,4,5]
When I query the db with Dapper, I would like dapper to deserialize to an object, MyType. Dapper wants MyArrayAsJson to be a string because it is, but I want it to deserialize to an int array. Is this possible?

public class JsonTypeHandler : SqlMapper.ITypeHandler
{
public void SetValue(IDbDataParameter parameter, object value)
{
parameter.Value = JsonConvert.SerializeObject(value);
}
public object Parse(Type destinationType, object value)
{
return JsonConvert.DeserializeObject(value as string, destinationType);
}
}
SqlMapper.AddTypeHandler(typeof(int[]), new JsonTypeHandler());

Dapper wants nothing to do with your fancy serialization shenanigans :) Basically, no: read it from the database as a string, then deserialize.
Adding an API that provided more direct / raw access to the incoming data as a BLOB/CLOB sequence would be nice, but it doesn't exist in Dapper today.

Dapper cannot do that, but you can do that!
It is just a little work using Dapper Multi Mapping
Select your [JsonColumn] as a string, and while mapping the results deserialize the JSON column, for example, if you have a Foo class and you want to deserialize the [JsonColumn] to it, do that:
using (var db = _context.Connect())
{
return await db.QueryAsync<Foo, string, Foo>(query, (foo, jsonColumnString) =>
{
return JsonSerializer.Deserialize<Foo>(jsonColumnString);
},
splitOn: "JsonColumn",
param: parameters);
}

Related

Converting simple Json to Complex Object in c#

My Json is
{
Id: 1,
Name:'Alex',
ConnectedObjectName:'Obj',
ConnectedObjectId: 99
}
I want this JSON to be converted to the following object
Class Response
{
int Id,
string Name,
ConnectedObject Obj
}
Class ConnectedObject
{
string COName,
int COId
}
Is there a way to achieve this using DataContractJsonSerializer
Most serializers want the object and the data to share a layout - so most json serializers would want ConnectedObjectName and ConnectedObjectId to exist on the root object, not an inner object. Usually, if the data isn't a good "fit" for what you want to map it into, the best approach is to use a separate DTO model that matches the data and the serializer, then map from the DTO model to the actual model you want programatically, or using a tool like auto-mapper.
If you used the DTO approach, just about any JSON deserializer would be fine here. Json.NET is a good default. DataContractJsonSerializer would be way way down on my list of JSONserializers to consider.
To expand on what Marc Gravell has said, most JSON serializers would expect your JSON to look like the following, which would map correctly to your classes:
{
"Id": 1,
"Name": "Alex",
"Obj" : {
"COName": "Obj",
"COId": 99
}
}
The way to get around this would be to read your JSON into an object that matches it, and then map it form that object to the object that you want:
Deserialize to this object:
class COJson {
public int Id;
public string Name;
public string ConnectedObjectName;
public int ConnectedObjectId;
}
Then map to this object:
class Response {
public int Id;
public string Name;
public ConnectedObject Obj;
}
class ConnectedObject {
public string COName;
public string COId;
}
Like this:
using(var stream = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
var serializer = new DataContractJsonSerializer(typeof(COJson));
var jsonDto = (COJson)ser.ReadObject(stream);
var dto = new Response(){
Id = jsonDto.Id,
Name = jsonDto.Name,
Obj = new ConnectedObject(){
COName = jsonDto.ConnectedObjectName,
COId = jsonDto.ConnectedObjectId
}
};
return dto;
}

Retrieving Data from LINQ to SQL in JSON format?

I have a problem with Serialization of Data from database to JSON format. I'm using WebAPI 2 and Entity Framework 6. I've created with EF a Model. The database and the tables with content are even created.
When I use the code below I'm getting an error when I type http://localhost:54149/api/qr_group .
Controller:
private EfContext db = new EfContext();
// GET api/<controller>
public IEnumerable<QR_Group> GetGroups()
{
var result = from g in db.QR_Groups
select g;
return result.ToList();
}
I don't know how to use Newtonsoft.Json to serialize the table with the content in JSON format.
I've tried the following code instead of the code above:
public IQueryable<QR_Group> GetGroups()
{
var groupList = (from g in db.QR_Groups
select new
{
name = g.name,
code = g.code
}).AsQueryable();
var json = JsonConvert.SerializeObject(groupList);
return json; //error CS0029: Cannot implicitly convert type `string' to `System.Linq.IQueryable<RestApi.Models.QR_Group>'
}
I get the error CS0029.. How can I solve this to return the data in json? As reminder: The QR_Group entity has 3 columns (Id, name, code)
Specifically to your second function, JsonConvert.SerializeObject would just serialize any object into a JSON format string, which means you should return a string instead of an IQueryable<>.
So for the controller there are quite a few ways to return it back, like: In MVC, how do I return a string result?
EDIT:
Following code would be one way that should working:
Controller:
private EfContext db = new EfContext();
// GET api/<controller>
public ActionResult GetGroups()
{
var groupList = (from g in db.QR_Groups
select new
{
name = g.name,
code = g.code
}).AsQueryable();
return Content(JsonConvert.SerializeObject(groupList));
}
you didn't mention the error when you are returning list from the api
controller. as i assume that you are returning a database entity that
in simple case not allow you as json result, because your entity may
be linked with other entities in the database.
you can check this link for reference
Now modify your code to something like below. It is just to make you understand.
Define a Type say QR_Group_Return
public class QR_Group_Return{
public string name{ get; set;}
public string code{ get; set;}
// Some other properties will go here.
}
Now instead of returning IEnumerable<QR_Group> we will return IEnumerable<QR_Group_Return>
private EfContext db = new EfContext();
// GET api/<controller>
public IEnumerable<QR_Group_Return> GetGroups()
{
var result = (from g in db.QR_Groups
select new QR_Group_Return {
name = g.name,
code = g.code,
// map your other properties also
}).ToList();
return result;
}
Hope this helps

Separating array and element from a JSON string

I am connecting you Google Places API to retrive results in the form of a JSON string. You can view the complete format of the string Here.
If you a look at it you will see that the actual results array starts after two elements which are html_attributions and next_page_token.
So When i try to deserialize it in this way:
var serializer = new JavaScriptSerializer();
var arr= serializer.Deserialize(result,typeof(string[]));
I get an empty array.
My question is how is there a way i can separate html_attributions and next_page_token fields and the pass the valid results array from the string to be deserialized?
I don't understand the part where you wish to seperate the html_attributions and the next_page_token.
Wouldn't it be sufficient to just deserialize the response with whatever properties that you need?
For example, you can deserialize the response to only retrieve the values that you desire;
// I represent the wrapper result
class Result
{
public List<string> html_attributions { get; set; }
public string next_page_token { get; set; }
public List<ResultItem> results { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
}
// the actual deserialization
Result Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
return serializer.Deserialize(json, typeof(Result));
}
Edit:
The reason that your deserialization doesn't return you a array of strings is because the response that you retrieve is infact an object and not an array, however the parameter within that object which is named results is an array. In order for you to deserialize more properties you'll have to define them in your "ResultItem" class, sorry for my poor naming here.
For instance, if you'd wish to also retrieve the icon property per result you'll have to add a property named "icon" of type string.
Meanwhile the property "photos" is an array, in order to deserialize it you'll have to create another class and add a property of type list/array of that newly created class, and it has to be named "photos" unless you use a different serializer or use DataContract and DataMember attributes (using the Name property for field mapping).
// the representation of a photo within a result item
class Photo
{
public int height { get; set; }
public List<string> html_attributions { get; set; }
public string photo_reference { get; set; }
public int width { get; set; }
}
// I represent a result item
class ResultItem
{
public string id { get; set; }
public string name { get; set; }
// the added icon
public string icon { get; set; }
// the added photos collection, could also be an array
public List<Photo> photos { get; set; }
}
Just look at the JSON result to figure out what other properties that you might want to add, for instance the "scope" property is an string whilst the "price_level" is an integer.
If I understand your comment correctly you're only interested in the actual results, you'll still have to deserialize the response correctly with its wrapper.
// the actual deserialization
List<ResultItem> Deserialize(string json)
{
var serializer = new JavaScriptSerializer();
var result = serializer.Deserialize(json, typeof(Result));
return result.results;
}
Edit2:
If you really want a string[] as a result you could simply take use of System.Linq using the code above.
string[] stringArray = result.results.Select(r => string.Format("id:{0} - name:{1}", r.id, r.name)).ToArray();
Edit3:
Instead of using the JavascriptSerializer you could use JObject functionality which can be found in the Newtonsoft.Json.Linq library.
var jsonObject = JObject.Parse(json);
string[] results = jsonObject.SelectTokens("results").Select(r => r.ToString()).ToArray();
This will give you an array of strings where each value within the array is the actual json string for each result.
If you however would like to query for the coordinates only:
var jsonObject = JObject.Parse(json);
var coordinates = jsonObject["results"]
.Select(x => x.SelectToken("geometry").SelectToken("location"))
.Select(x => string.Format("{0},{1}", (string)x.SelectToken("lat"), (string)x.SelectToken("lng")))
.ToArray();
This would give you an array of coordinates, eg:
[
"-33.867217,151.195939",
"-33.866786,151.195633",
...
]
Whatever approach you choose you'll be able to accomplish same results using either Newtonsoft or the .net serializer, whilst the Newtonsoft approach would allow you to query without creating strong types for deserialization.
I don't find the point of "[...] pass the valid results array from the string to be deserialized".
Maybe you need to switch to JSON.NET and do something like this:
// You simply deserialize the entire response to an ExpandoObject
// so you don't need a concrete type to deserialize the whole response...
dynamic responseEntity = JsonConvert.DeserializeObject<ExpandoObject>(
googlePlacesJson, new ExpandoObjectConverter()
);
// Now you can access result array as an `IEnumerable<dynamic>`...
IEnumerable<dynamic> results = responseEntity.results;
foreach(dynamic result in results)
{
// Do stuff for each result in the whole response...
}

Manipulate Values of Stringfied values on C#

I have a below code that used JSON.stringify to the object then passed it on POST method (Please see below Javascript code). I'm getting those values on the backend using C#. My problem is, how could I convert/manipulate/access the stringified values. Please see below C# code
Javascript:
var json_db = JSON.stringify(selectedDbInfo);
$.post("../FormActions/DatabaseChanges.aspx", { action: "savedb", orderNumber: orderNumber, selectedDb: json_db},
function (response) {
alert('ok');
});
C#:
var dbValue = c.Request.Params["selectedDb"];
below is the result value of dbValue
"[{\"dbname\":\"BASINS\",\"distance\":\"0\"},{\"dbname\":\"BROWNFIELD\",\"distance\":\"0.5\"},{\"dbname\":\"BRS\",\"distance\":\"0\"}]"
You need to parse the JSON into a .NET array or List.
Many use json.NET for this: http://james.newtonking.com/json
At a push you could use some string manipulation to populate your objects one by one, but I wouldn't recommend that.
There are many samples here on SO.
if you're just want to convert it dictionary look here:
How can I deserialize JSON to a simple Dictionary<string,string> in ASP.NET?
However, there's a built in mechanism in ASP.NET MVC that serializes automatically your json param to predefined objects at your convenient.
You may define a class having the fields like dbname and distance as properties. Then you may deserialize the json string dbValue into a list of that type using NewtonSoft.Json. Please see the code below:
var list = JsonConvert.DeserializeObject<List<RootObject>>(dbValue);
foreach (var item in list)
{
Console.WriteLine(string.Format("dbname: {0}, distance: {1}", item.dbname, item.distance));
}
Ans the definition of RootObject is as simple as you guess:
public class RootObject
{
public string dbname { get; set; }
public string distance { get; set; }
}
Create a custom serializable data contract class, say DatabaseDistance, with following properties:
[DataMember(Name = "dbname")]
private string name;
[DataMember(Name = "distance")]
private double distance;
and use following method for deserialization:
public static T FromJSON<T>(string jsonValue, IEnumerable<Type> knownTypes)
{
//validate input parameters here
T result = default(T);
try
{
using (MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(jsonValue)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), knownTypes);
result = (T)serializer.ReadObject(stream);
}
}
catch (Exception exception)
{
throw new Exception("An error occurred while deserializing", exception);
}
return result;
}
pass list of your objects as type parameter

How to map strings/poor JSON to C# object members? (and ultimately to a MySQL database)

(This is all kind of background to give you context around my problem. You can skip down to "The Problem" and read that, and then maybe come back up and skim the background if you want to get straight to the point. Sorry it's a wall of text!)
I've got a bunch of terrible, terrible JSON I need to store in a database. Essentially, someone took a large XML file, and serialized it to one, big, flat JSON object by simply using the XML's XPath. Here's an example of what I mean:
Original XML:
<statistics>
<sample>
<date>2012-5-10</date>
<duration>11.2</duration>
</sample>
<sample>
<date>2012-6-10</date>
<duration>13.1</duration>
</sample>
<sample>
<date>2012-7-10</date>
<duration>10.0</duration>
</sample>
</statistics>
The Horrible JSON I Have to Work With: (basically just the XPath from above)
{
"statistics":"",
"statistics/sample":"",
"statistics/sample/date":"2012-5-10",
"statistics/sample/duration":"11.2",
"statistics/sample#1":"",
"statistics/sample/date#1":"2012-6-10",
"statistics/sample/duration#1":"13.1",
"statistics/sample#2":"",
"statistics/sample/date#2":"2012-7-10",
"statistics/sample/duration#2":"10.0",
}
And now I need to put it in a database that contains a statistics table with date and duration columns.
How I'm Currently Doing It: (or at least a simple example of how)
Tuple<string, string>[] jsonToColumn = // maps JSON value to SQL table column
{
new Tuple<string, string>("statistics/sample/date", "date"),
new Tuple<string, string>("statistics/sample/duration", "duration")
};
// Parse the JSON text
// jsonText is just a string holding the raw JSON
JavaScriptSerializer serializer = new JavaScriptSerializer();
Dictionary<string, object> json = serializer.DeserializeObject(jsonText) as Dictionary<string, object>;
// Duplicate JSON fields have some "#\d+" string appended to them, so we can
// find these and use them to help uniquely identify each individual sample.
List<string> sampleIndices = new List<string>();
foreach (string k in json.Keys)
{
Match m = Regex.Match(k, "^statistics/sample(#\\d*)?$");
if (m.Success)
{
sampleIndices .Add(m.Groups[1].Value);
}
}
// And now take each "index" (of the form "#\d+" (or "" for the first item))
// and generate a SQL query for its sample.
foreach (string index in compressionIndices)
{
List<string> values = new List<string>();
List<string> columns = new List<string>();
foreach (Tuple<string, string> t in jsonToColumn)
{
object result;
if (json.TryGetValue(t.Item1 + index, out result))
{
columns.Add(t.Item2);
values.Add(result);
}
}
string statement = "INSERT INTO statistics(" + string.Join(", ", columns) + ") VALUES(" + string.Join(", ", values) + ");";
// And execute the statement...
}
However, I'd like to use an ADO.NET Entity Data Model (or something LINQ-ish) rather than this hackery, because I need to start performing some queries before inserting and apply some updates, and creating and executing my own SQL statements is just... cumbersome. I created an ADO.NET Entity Data Model (.edmx) file and set things up, and now I can easily use this model to interact with and write to my database.
The Problem/Question
The problem is I'm not sure how to best map from my JSON to my ADO.NET Entity Data Model Statistic object (that represents a sample/record in the statistics table). The easiest would be to change my Tuple list to use something like pointers-to-members (a la Tuple<string, Statistic::*Duration>("statistics/sample/duration", &Statistic::Duration) if this were C++), but a) I don't even think this is possible in C#, and b) even if it was it makes my Tuples all have different types.
What are some of my options here? How can I best map the JSON to my Statistic objects? I'm kinda new to the LINQ world, and am wondering if there's a way (through LINQ or something else) to map these values.
It's a sub-optimal position I'm in (working with such poor JSON), and I recognize it's possible that maybe my current method is better than anything else given my situation, and if that's the case I'd even accept that as my answer. But I would really like to explore what options there are for mapping this JSON to a C# object (and ultimately to the SQL database).
If the whole problem is mapping that "JSON" you have to POCO entities, here's an example on how to Deserialize it using a Custom JavascriptConverter:
Your POCO entities:
public class Statistics
{
public Statistics()
{
Samples = new List<Sample>();
}
public List<Sample> Samples { get; set; }
}
public class Sample
{
public DateTime Date { get; set; }
public float Duration { get; set; }
}
Your StatsConverter:
public class StatsConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
else if (type == typeof(Statistics))
{
Statistics statistics = null;
Sample sample = null;
{
foreach (var item in dictionary.Keys)
{
if (dictionary[item] is string && item.Contains("duration"))
sample.Duration = float.Parse(dictionary[item].ToString());
else if (dictionary[item] is string && item.Contains("date"))
sample.Date = DateTime.Parse((dictionary[item].ToString()));
else if (dictionary[item] is string && item.Contains("sample"))
{
sample = new Sample();
statistics.Samples.Add(sample);
}
else if (dictionary[item] is string && item.Contains("statistics"))
statistics = new Statistics();
}
}
return statistics;
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new Type[] { typeof(Statistics)})); }
}
}
Now a sample on how to Deserialize it:
string json = #"{
""statistics"":"""",
""statistics/sample"":"""",
""statistics/sample/date"":""2012-5-10"",
""statistics/sample/duration"":""11.2"",
""statistics/sample#1"":"""",
""statistics/sample/date#1"":""2012-6-10"",
""statistics/sample/duration#1"":""13.1"",
""statistics/sample#2"":"""",
""statistics/sample/date#2"":""2012-7-10"",
""statistics/sample/duration#2"":""10.0""
}";
//These are the only 4 lines you'll require on your code
JavaScriptSerializer serializer = new JavaScriptSerializer();
StatsConverter sc = new StatsConverter();
serializer.RegisterConverters(new JavaScriptConverter[] { new StatsConverter() });
Statistics stats = serializer.Deserialize<Statistics>(json);
stats object above will deserialize to a Statistics object with 3 Sample objects in its Samples collection.
If you use a ORM (e.g. EntityFramework) then you are just working against a datamodel so assuming you would then have a model class defined something like...
public class Sample
{
public string Date { get; set; }
public double Duration { get; set; }
}
Then you could do something like this...
List<Sample> samples = new List<Sample>();
Dictionary<string, object> data = ser.DeserializeObject(json) as Dictionary<string, object>;
var keys = data.Keys.ToList();
for (int i = 0; i <keys.Count; i++)
{
string k = keys[i];
if (Regex.IsMatch(k, "^statistics/sample(#\\d*)?$"))
{
samples.Add(new Sample
{
Date = (string)data[keys[i + 1]],
Duration = double.Parse((string)data[keys[i + 2]])
});
}
}
I just populate a list for the example and again if you are using something like EntityFramework then you could could just be adding the instances directly to your repository/datacontext.

Categories

Resources