Dynamically switch applicable DataContract at runtime? - json

Can I turn data contract attributes off and on dynamically? Essentially, I'd like two data contracts, one between the 3rd party and my app, and one between my app and my client - without having to manage two DTO classes. For example, consider MyDTO:
[DataContract]
public class MyDTO
{
[DataMember(Name = "Lame/3rdParty/Inbound/Key")]
public string MyCoolOutboundKey { get; set; }
}
I'd like to deserialize the DTO with ServiceStack.Text:
MyDTO dto = "{\"Lame/3rdParty/Inbound/Key\":\"CoolValue\"}".FromJson<MyDTO>();
But, I'd like to serialize it so that this Assertion would be true:
Assert.AreEqual("{\"MyCoolOutboundKey\":\"CoolValue\"}",dto.ToJson());
The actual object in question has over hundred properties, so I'm hoping to avoid having to create a second class just to allow for outbound serialization.

So, there is nothing wrong with your approach, but I think you are underestimating the power of JsonObject, it's kinda like LINQ to JSON.
I used it to get geo data from Google Maps and map it to a nice little DTO:
var jsonObj = jsonFromGoogleMaps.FromJson<JsonObject>();
return (
from x in jsonObject.Get<JsonArrayObjects>("results")
let geo = x.Get("geometry")
let loc = geo.Get("location")
return new Coords
{
Lat = x.Get<decimal>("lat"),
Lng = x.Get<decimal>("lng"),
}
).FirstOrDefault();

OK - it's pretty well established that you cannot change attributes at runtime.
An alternative that would create an end-run around the entire issue, would be to pre-process the incoming json, replacing the keys according to a map, i.e.:
Dictionary<String,String> map = new Dictionary<String,String>();
map.Add("Lame/3rdParty/Inbound/Key","MyCoolOutboundKey");
JsonObject result = new JsonObject();
JsonObject obj = jsonObject.Parse("{\"Lame/3rdParty/Inbound/Key\":\"CoolValue\"}");
foreach (var entry in obj)
{
entry.Key = map[entry.Key];
result[entry.Key] = entry.Value;
}
Assert.AreEqual("{\"MyCoolOutboundKey\":\"CoolValue\"}",result.ToJson());
This way the only data contract I'd require would be the one between my app and my app's clients.

Related

Omit null values in json response from Nancy FX

I am trying to create a REST service using Nancy FX in a C# environment. I can easily do a Response.AsJson and it all looks good. But I want the response to omit any properties that are null.
I have not been able to figure out how to do this yet.
Could someone point to me towards a help document or a blog post somewhere, that explains how to do this.
Thanks,
JP
I would create a dynamic anonymous type and return that. So let's say you have a User object like this:
public class User
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
You want to pass back an instance of this type as a JSON response so you will have some code like this:
Get["/user/{userid}"] = parameters =>
{
var user = UserService.GetById(Db, (string)parameters.userid);
if (user == null) return HttpStatusCode.UnprocessableEntity;
return Response.AsJson(user);
};
But you don't want to return the User instance, instead you want to return an separate instance of a dynamic type that will only implement a property if the property value is not null for a given instance.
So I would suggest code something like this:
Get["/user/{userid}"] = parameters =>
{
var user = UserService.GetById(Db, (string)parameters.userid);
if (user == null) return HttpStatusCode.UnprocessableEntity;
dynamic userDTO = new ExpandoObject();
userDTO.Id = user.Id;
if (!string.IsNullOrEmpty(user.FirstName)) userDTO.FirstName = user.FirstName;
if (!string.IsNullOrEmpty(user.LastName)) userDTO.Lastname = user.LastName;
return Response.AsJson((ExpandoObject)userDTO);
};
Note 1
You don't need to test for the Id since that is implied by the successful return of the User instance from the database.
Note 2
You need to use a dynamic type so you can include ad hoc properties. The problem is that extension methods cannot accept dynamic types. To avoid this you need to declare it as an ExpandoObject but use it as a dynamic. This trick incurs a processing overhead but it allows you to cast the dynamic to an ExpandoObject when passing it in to the AsJson() extension method.

Returning and displaying a JSON Object which is an associate array in a Windows 8 App

I am using a online tutorial to lean how to create a web service, generate a JSON object, send it back to my Win 8 App and display it. The web service is working however I am struggling to return a value to the APP. My code in the app is:
WinJS.xhr({
url: 'http://localhost/filmgloss/web-service.php?termID=1&format=JSON'
})
.done(
function complete(result) {
// terms is the key of the object
for (var terms in result) {
for (var term in terms) {
if (result.hasOwnProperty(term)) {
//here you have to acess to
var termName = result[term].termName;
var def = result[term].definition;
}
//Show Terms
testDef.innerText = definition;
}
}
},
And he code in my web service is:
if($format == 'json') {
header('Content-type: application/json');
echo json_encode(array('terms'=>$terms));
}else...
The JSON output itself looks like:
{"terms":[{"term":{ "termName":"Focus","definition":"A Focus..."}}]}
I am using a for..in but whilst I can look inside terms' I can't work out how to look interm`
I usually build my own data structure that represents the JSON structure.
In your case it would be something like this:
public class TermsList
{
public List<Term> terms { get; set; }
}
public class Term
{
public string termName { get; set }
public definition termName { get; set }
...
}
Then you can just deserialize your string into your object. There are different ways to do this. I would use Json.Net.
Here is one way:
Parse JSON in C#
public static T Deserialise<T>(string json)
{
T obj = Activator.CreateInstance<T>();
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(obj.GetType());
obj = (T)serializer.ReadObject(ms); // <== Your missing line
return obj;
}
}
If you wanna keep it dynamic, that should work too:
http://www.drowningintechnicaldebt.com/ShawnWeisfeld/archive/2010/08/22/using-c-4.0-and-dynamic-to-parse-json.aspx
I have managed to resolve my issue with the help of a developer friend. My problem was that I had not realised that the result of WinHS.xhr was not already a JSON Array. Although my web-service outputs a JSON Array when it is consumed through WinHS.xhr it appears to be returned as an XMLHttpRequest object.
The solution was therefore to process the result using:
JSON.parse(result.responseText)
I could then use a For...In loop as expected:
for (terms in responseTerms) {
//terms will find key "terms"
var termName = responseTerms.terms[0].term.termName;
var termdefinition = responseTerms.terms[0].term.definition;
testTerm.innerText = termName;
testDef.innerText = termdefinition;
}
Thanks for everyone that commented, hopefully this may help others in the future if they're starting out with Win 8 app development.

Can RestSharp send a List<string> in a POST request?

I am trying to get RestSharp to work with a restful service that I have. Everything seems to be working fine, except when my object being passed via POST contains a list (in this particular case a list of string).
My object:
public class TestObj
{
public string Name{get;set;}
public List<string> Children{get;set;}
}
When this gets sent to the server the Children property gets sent as a string with the contents System.Collections.Generic.List`1[System.String].
This is how I am sending the object:
var client = new RestClient();
var request = new RestRequest("http://localhost", Method.PUT);
var test = new TestObj {Name = "Fred", Children = new List<string> {"Arthur", "Betty"}};
request.AddObject(test);
client.Execute<TestObj>(request);
Am I doing something wrong, or is this a bug in RestSharp? (If it makes a difference, I am using JSON, not XML.)
It depends on what server you're hitting, but if you're hitting an ASP.NET Web API controller (and probably other server-side technologies), it'll work if you add each item in the collection in a loop:
foreach (var child in test.Children)
request.AddParameter("children", x));
Use AddJsonBody
var client = new RestClient();
var request = new RestRequest("http://localhost", Method.PUT);
request.AddJsonBody(new TestObj {
Name = "Fred",
Children = new List<string> {"Arthur", "Betty"}
});
client.Execute(request);
Api Side
[AcceptVerbs("PUT")]
string Portefeuille(TestObj obj)
{
return String.Format("Sup' {0}, you have {1} nice children",
obj.Name, obj.Children.Count());
}
I had a similar problem with a list of Guids. My post would work but the list would never have the correct data. I hacked it abit and used the json.net to serialize the object
Issue I had on another stackoverflow post
I know this isn't perfect but does the trick

serializing dates correctly with javascript serializer in asp.net mvc3

i am experimenting with backbone javascript after seeing the Tekpub MVC3 screencasts by Rob Connery
i like his Massive database access, but as soon as it is getting a bit more complex than a video can possibly show you.
i added extra fields to my database, being datetime fields.
however, this javascript serializer, converts them into strings
public string toJson(dynamic content) {
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new ExpandoObjectConverter() });
var json = serializer.Serialize(content);
return json.ToString();
}
this makes a datetime from this: {19/10/2011 1:58:27} into this: "19/10/2011" (*values taken from the quickwatch window on runtime..., basicly comes down to a loss in precision and it now being a basic string.
after backbone pushes that back to the server (on a model.save() call), i try to update the model like Rob does:
[HttpPut]
public ActionResult Edit()
{
var model = SqueezeJson();
model.UpdatedAt = DateTime.Now;
_movies.Update(model, model.Id);
return CmoJSON(model);
}
for the SqueezeJson function, check his source
resulting in an error like this:
Arithmetic overflow error converting expression to data type datetime.
i kind of expected this to happen since i noticed the dates being dumped into strings, i had no idea how it would go back into a date time using massive.
has anyone worked with massive and dates, in a context like this (serializing to and from json)?
i know the problem isn't necessarily massive itself, it's the json serializiation that dumbs it down into a string with loss of data, and doesn't return it to a proper date.
but still, maybe someone has a better way of doing this...
any idea's are welcome...
I have encountered the same question with you.
You can change Serialize method in ExpandoObjectConverter like:
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
ExpandoObject expando = (ExpandoObject) obj;
if(expando!=null)
{
Dictionary<string,object> result = new Dictionary<string, object>();
foreach (KeyValuePair<string, object> item in expando)
{
var value = item.Value ?? "";
if (value is DateTime)
result.Add(item.Key, ((DateTime) value).ToString("yyyy.MM.dd"));
else
{
result.Add(item.Key, value.ToString());
}
}
return result;
}
return new Dictionary<string, object>();
}

WCF restful returning JSON by using Entity Framework Complex

recently I have set up a WCF restful service with EF4.
It all worked out when returning XML format response. however when it comes to JSON, I got 504 Error. unable to return json data, WCF Resful Service .NET 4.0
By digging deeper by using Service Trace Viewer:
I found this error:
'The type 'xxx.DataEntity.AppView'
cannot be serialized to JSON because
its IsReference setting is 'True'. The
JSON format does not support
references because there is no
standardized format for representing
references. To enable serialization,
disable the IsReference setting on the
type or an appropriate parent class of
the type.'
The "AppView" is a complex object class which generated by EF4 from a store procedure.
I spend quite a bit time google how to disable the IsReference, very little result so far.
anyone? with any solutions?
thanks in advance
Code:
[OperationContract]
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "App/{id}/{format}")]
AppView FuncDetail(string id, string format);
public AppView FuncDetail(string id, string format)
{
SetResponseFormat(format);
return AppSvcs.GetById(id);
}
private void SetResponseFormat(string format)
{
if (format.ToLower() == "json")
{
ResponseContext.Format = WebMessageFormat.Json;
}
else
{
ResponseContext.Format = WebMessageFormat.Xml;
}
}
I ran into exactly the same issue. It only happened on one of my service methods where I was trying to return JSON serialised Entity objects. For all my other methods I was returning JSON serialised data transfer objects (DTOs), which are stand-alone and not connected to the Entity framework. I am using DTOs for data posted into methods. Often, the data you send out does not need all the data you store in the model or the database e.g. ID values, updated dates, etc. The mapping is done in the model class, like so:
public partial class Location
{
public static LocationDto CreateLocationDto(Location location)
{
LocationDto dto = new LocationDto
{
Accuracy = location.Accuracy,
Altitude = location.Altitude,
Bearing = location.Bearing
};
return dto;
}
It may seem a bit clunky but it works and it ensures that you only send the data fields you intended to send back. It works for me because I only have 5 or 6 entities but I can see that it would get a bit tedious if you have lots of classes.
I was running into the same problem, as caused by using the auto-generated ADO Entity Models. I have not found a direct fix for this issue, but as a work around, I serialize the response as json explicitly.
So in your example, the AppView FuncDetail looks like this:
public object FuncDetail(string id, string format)
{
SetResponseFormat(format);
// where AppSvc is the object type and the enumerable list of this type is returned by the GetById method, cast it to a json string
return JSONSerializer.ToJson<AppSvc>(AppSvcs.GetById(id));
}
Here are the Serializers that I'm using:
public static class GenericSerializer
{
public static DataTable ToDataTable<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = new DataTable();
// column names
PropertyInfo[] oProps = null;
if (varlist == null) return dtReturn;
foreach (T rec in varlist)
{
// Use reflection to get property names, to create table, Only first time, others will follow
if (oProps == null)
{
oProps = ((Type)rec.GetType()).GetProperties();
foreach (PropertyInfo pi in oProps)
{
Type colType = pi.PropertyType;
if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition()
== typeof(Nullable<>)))
{
colType = colType.GetGenericArguments()[0];
}
dtReturn.Columns.Add(new DataColumn(pi.Name, colType));
}
}
DataRow dr = dtReturn.NewRow();
foreach (PropertyInfo pi in oProps)
{
dr[pi.Name] = pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue
(rec, null);
}
dtReturn.Rows.Add(dr);
}
return dtReturn;
}
}
public static class JSONSerializer
{
public static string ToJson<T>(IEnumerable<T> varlist)
{
DataTable dtReturn = GenericSerializer.ToDataTable(varlist);
return GetJSONString(dtReturn);
}
static object RowsToDictionary(this DataTable table)
{
var columns = table.Columns.Cast<DataColumn>().ToArray();
return table.Rows.Cast<DataRow>().Select(r => columns.ToDictionary(c => c.ColumnName, c => r[c]));
}
static Dictionary<string, object> ToDictionary(this DataTable table)
{
return new Dictionary<string, object>
{
{ table.TableName, table.RowsToDictionary() }
};
}
static Dictionary<string, object> ToDictionary(this DataSet data)
{
return data.Tables.Cast<DataTable>().ToDictionary(t => "Table", t => t.RowsToDictionary());
}
public static string GetJSONString(DataTable table)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(table.ToDictionary());
}
public static string GetJSONString(DataSet data)
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(data.ToDictionary());
}}
It is a lot clearer to use Entity Metadata instead of Reflection.
The Metadata is pretty extensive.
another way to do this is to use LINQ to create an anonymous type with the subset of fields that you need from your entity and then use JSON.NET to serialize the collection of anon types that you created in the LINQ statement. then persist that collection out as a string by serializing.