ASP.NET WebApi and Partial Responses - json

I have a ASP.NET WebApi project that I am working on. The boss would like the returns to support "partial response", meaning that though the data model might contain 50 fields, the client should be able to request specific fields for the response. The reason being that if they are implementing for example a list they simply don't need the overhead of all 50 fields, they might just want the First Name, Last Name and Id to generate the list. Thus far I have implemented a solution by using a custom Contract Resolver (DynamicContractResolver) such that when a request comes in I am peeking into it through a filter (FieldListFilter) in the OnActionExecuting method and determining if a field named "FieldList" is present and then if it is I am replacing the current ContractResolver with a new instance of my DynamicContractResolver and I pass the fieldlist to the constructor.
Some sample code
DynamicContractResolver.cs
protected override IList<JsonProperty> CreateProperties(Type type, Newtonsoft.Json.MemberSerialization memberSerialization)
{
List<String> fieldList = ConvertFieldStringToList();
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
if (fieldList.Count == 0)
{
return properties;
}
// If we have fields, check that FieldList is one of them.
if (!fieldList.Contains("FieldList"))
// If not then add it, FieldList must ALWAYS be a part of any non null field list.
fieldList.Add("FieldList");
if (!fieldList.Contains("Data"))
fieldList.Add("Data");
if (!fieldList.Contains("FilterText"))
fieldList.Add("FilterText");
if (!fieldList.Contains("PageNumber"))
fieldList.Add("PageNumber");
if (!fieldList.Contains("RecordsReturned"))
fieldList.Add("RecordsReturned");
if (!fieldList.Contains("RecordsFound"))
fieldList.Add("RecordsFound");
for (int ctr = properties.Count-1; ctr >= 0; ctr--)
{
foreach (string field in fieldList)
{
if (field.Trim() == properties[ctr].PropertyName)
{
goto Found;
}
}
System.Diagnostics.Debug.WriteLine("Remove Property at Index " + ctr + " Named: " + properties[ctr].PropertyName);
properties.RemoveAt(ctr);
// Exit point for the inner foreach. Nothing to do here.
Found: { }
}
return properties;
}
FieldListFilter.cs
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
// We need to determine if there is a FieldList property of the model that is being used.
// First get a reference to the model.
var modelObject = actionContext.ActionArguments.FirstOrDefault().Value;
string fieldList = string.Empty;
try
{
// Using reflection, attempt to get the value of the FieldList property
var fieldListTemp = modelObject.GetType().GetProperty("FieldList").GetValue(modelObject);
// If it is null then use an empty string
if (fieldListTemp != null)
{
fieldList = fieldListTemp.ToString();
}
}
catch (Exception)
{
fieldList = string.Empty;
}
// Update the global ContractResolver with the fieldList value but for efficiency only do it if they are not the same as the current ContractResolver.
if (((DynamicContractResolver)GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver).FieldList != fieldList)
{
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DynamicContractResolver(fieldList);
}
}
I can then send a request with the json content payload looking as such:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"Id":1234
},
{
"Id":1235
}
]
}
and I will receive a response like so:
{
"FieldList":"NameFirst,NameLast,Id",
"Data":[
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1234
},
{
"NameFirst":"Brian",
"NameLast":"Mueller",
"Id":1235
}
]
}
I believe that using the ContractResolver might run into threading issues. If I change it for one request is it going to be valid for all requests thereafter until someone changes it on another request (seems so through testing) If that is the case, then I don't see the usefulness for my purpose.
In summary, I am looking for a way to have dynamic data models such that the output from a request is configurable by the client on a request by request basis. Google implements this in their web api and they call it "partial response" and it works great. My implementation works, to a point but I fear that it will be broken for multiple simultaneous requests.
Suggestions? Tips?

A simpler solution that may work.
Create a model class with all 50 members with nullable types.
Assign values to the requested members.
Just return the result in the normal way.
In your WebApiConfig.Register() you must set the null value handling.
config.Formatters.JsonFormatter.SerializerSettings =
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };

You must not touch the configuration. You need the contract resolver on per-request basis. You can use it in your action method like this.
public class MyController : ApiController
{
public HttpResponseMessage Get()
{
var formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.ContractResolver =
new DynamicContractResolver(new List<string>()
{"Id", "LastName"}); // you will get this from your filter
var dto = new MyDto()
{ FirstName = "Captain", LastName = "Cool", Id = 8 };
return new HttpResponseMessage()
{
Content = new ObjectContent<MyDto>(dto, formatter)
};
// What goes out is {"LastName":"Cool","Id":8}
}
}
By doing this, you are locking yourself into JSON content type for response messages but you have already made that decision by using a Json.NET specific feature. Also, note you are creating a new JsonMediaTypeFormatter. So, anything you configure to the one in the configuration such as media type mapping is not going to be available with this approach though.

I know this question is from many years ago, but if you're looking to do this with modern releases of the framework, I'd recommend nowadays to use OData services (http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/using-select-expand-and-value).

Related

MvvmCross IMvxNavigationFacade, MvxViewModelRequest causes Init() to be called rather than Prepare()

I've implemented an IMvxNavigationFacade for deep linking in my MvvmCross 5.6.x sample app. I've added logic in BuildViewModelRequest() to construct a MvxViewModelRequest with parameters passed in as MvxBundle.
if (url.StartsWith("http://www.rseg.net/rewards/"))
{
var parametersBundle = new MvxBundle();
var id = url.Substring(url.LastIndexOf('/') + 1);
parametersBundle.Data.Add("id", id);
return Task.FromResult(
new MvxViewModelRequest(typeof(RewardDetailViewModel),
parametersBundle, null));
}
However, this approach causes the old style Init() method to be called in the target ViewModel rather than the new typesafe Prepare() method.
public class RewardDetailViewModel :
MvxViewModel<RewardDetailViewModel.Parameteres>
{
...
public new void Init(string id)
{
if (!string.IsNullOrWhiteSpace(id))
{
if (int.TryParse(id, out _rewardId))
RaiseAllPropertiesChanged();
}
}
public override void Prepare(Parameteres parameter)
{
if (parameter != null)
{
_rewardId = parameter.RewardId;
RaiseAllPropertiesChanged();
}
}
}
Is there a way to construct a MvxViewModelRequest so that you pass in an instance of the parameter class for the target ViewModel causing the Prepare() method to be called?
The entire solution can be viewed on GitHub https://github.com/rsegtx/So.MvvmNav2
Thanks in advance!
After doing some research I found at lease one way to accomplish this.
Create a ViewModelInstanceRequest rather than a ViewModelRequest so that you can call ViewModelLoader.LoadViewModel passing in a parameters object; the ViewModelRequest only allows parameters to be passed using a MvxBundle. Make the following change to BuildViewModelRequest() on the NavigationFacade:
var request = new
MvxViewModelInstanceRequest(typeof(RewardDetailViewModel));
var parameters = new RewardDetailViewModel.Parameteres();
.... parse parameters and fill in parameters object
request.ViewModelInstance = ViewModelLoader.LoadViewModel(
request, parameters, null);
return Task.FromResult((MvxViewModelRequest)request);
Create your own IMvxNavigationService and add logic to inspect the object returned from the NavigationFacde and if it is a ViewModelInstanceRequest then use it as is rather than one previously creating.
var facadeRequest = await facade.BuildViewModelRequest(path,
paramDict).ConfigureAwait(false);
...
if (facadeRequest is MvxViewModelInstanceRequest)
request = facadeRequest as MvxViewModelInstanceRequest;
else
{
facadeRequest.ViewModelType = facadeRequest.ViewModelType;
if (facadeRequest.ParameterValues != null)
{
request.ParameterValues = facadeRequest.ParameterValues;
}
request.ViewModelInstance = ViewModelLoader.LoadViewModel(
request, null);
}
I've updated the original example on GitHub https://github.com/rsegtx/So.MvvmNav2.

Web Api 2 with OData v4 - Bound function returning complex object

In this simple example I am trying to get an object serialized as JSON from a Web Api 2 + OData v4 service. Controller has bound function Test which is returning an array of annon. objects.
public class ProductsController : ODataController
{
[HttpGet]
public IHttpActionResult Test(int key)
{
var res = new[]
{
new { Name = "a", Value = new[] { 1, 2, 3 } },
new { Name = "b", Value = new[] { 2, 4, 5 } }
// this also produces same result
// new { Name = "a", Value = "c" },
// new { Name = "b", Value = "c" }
};
return this.Ok(res);
}
}
Edm is built with this piece of code:
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Product>("Products");
var productType = builder.EntityType<Product>();
var f = productType.Function("Test").Returns<object>();
when I make a request to the service (eg. http://localhost:9010/odata/Products(33)/Default.Test) I am getting a strange response - an array of two empty objects, like this:
{
"#odata.context": "http://localhost:9010/odata/$metadata#Collection(System.Object)",
"value": [
{},
{}
]
}
In my real app I'm returning object serialized to a JSON string with Newtonsoft's Json converter - that works fine, but this problem is still bothering me. I suspect it is something related to OData's default serializer, but it is unclear to me how to configure it.
So, is it possible to configure edm function's return parameter in such way where I would get correctly serialized complex object?
Thanks!
As lukkea said, OData is not designed to work with anonymous types.
Side note, in you WebApiConfig you should change "Returns" to "ReturnsCollection" if you are returning a collection.
Anyway, let's assume you wrote the following.
return this.Ok(Newtonsoft.Json.JsonConvert.SerializeObject(res));
var f = productType.Function("Test").Returns<string>();
You would get back the following:
{
"#odata.context": "http://localhost/Test/odata/$metadata#Edm.String",
"value":
"[
{\"Name\":\"a\",\"Value\":[1,2,3]},
{\"Name\":\"b\",\"Value\":[2,4,5]}
]"
}
Note that there is still 2 items in the array but this time they are not empty.
Since OData did not know the return type in your previous example, it return 2 objects without values.
You have 2 options.
Return back the anonymous type as a serialized json string and then deserialize that json on the client side.
Create a class and return that type.
Option 1
// ON SERVER
return this.Ok(Newtonsoft.Json.JsonConvert.SerializeObject(res));
var f = productType.Function("Test").Returns<string>();
// ON CLIENT
string jsonString = odataContext.Products.ByKey(33).Test().GetValue();
var objectList = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(jsonString);
string firstObjectName = objectList[0].Name;
Option 2
// ON SERVER
public class TestObject
{
public string Name { get; set; }
public List<int> Integers { get; set; }
}
var res = new List<TestObject>
{
new TestObject { Name = "a", Integers = new List<int> { 1, 2, 3 } },
new TestObject { Name = "b", Integers = new List<int> { 2, 4, 5 } }
};
return this.Ok(res);
var f = productType.Function("Test").ReturnsCollection<TestObject>();
If you want to return a person with an extra property that is not strongly typed then you want ODataOpenType
Whilst it is true that working with dynamic responses is tricky, it's not that hard and you certainly do not need to resort to returning your objects through string encoding.
The key is that a dynamic response means that we can't use the standard EnableQueryAttribute to apply specific projections or filtering on the method response, and we can't return OkNegotiatedContentResult as this response object is designed to enable the runtime to manipulate how the response object is serialized into the HTTP response.
ApiController.Ok(T content);
Creates an System.Web.Http.Results.OkNegotiatedContentResult with the specified values.
content: The content value to negotiate and format in the entity body.
Content Negotiation
Content Negotiation is basically a mechansim to encapsulate the process to determine how your method response should be transmitted over http as well as the heavy lifting to physically encode the response.
By using Content Negotiation, your method only needs to return a query or raw c# object, even if the caller specified in the request that the output should be XML (instead of the standard JSON). The concept of dealing with the physical serialization and the logic to interpret the caller's intent is abstracted away so you do not need to worry about it at all.
There are 2 options that are available to you to customise the output:
ApiController.JsonResult(T content);
This allows you to specify the object graph to serialise, this will not respond to EnableQueryAttribute or Content Negotiation.
return this.JsonResult(res);
This response is not in the usual OData Response wrapper so it does not include the standard #odata attributes like #odata.context.
If your response object does not match the type specified in the Action/Function defintion in the OData Model, calls to this endpoint will result in a But if the response object does not match the type specified in the OData Model this will result in a HTTP 406 Not Acceptable, so make sure you register the response an an object or another type of interface that your response object inherits from or implements.
HttpResponseMessage
Bypass OData response management all together and return HttpResponseMessage directly from your method. In this way you are responsible for serializing the response content as well as the response headers.
This however bypasses all the OData mechanisms including response validation and formatting, meaning you can return whatever you want.
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(res))
};
result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
return result;

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.

How to configure Castle Windsor to dynamically pick the provider based upon arguments (other than "name") supplied to Resolve()

I am trying to learn how to use Castle Windsor IoC and am having some difficulties understanding how to configure some objects that I need to resolve dynamically. Basically, I have several implementations of IDataSource, and I need to choose the implementation to use based upon how a particular "data source" has been configured. So I might have quite alot of data sources configured to use one of the 3 implementations. My expectation is that the dependant code will take a dependency on a factory method which will give them the correct IDataSource when it is provided with the "data source id" along with the dependencies the data source implementations require (an IPrincipal).
I am struggling with how to correctly write the registration delegate for Windsor. Below is roughly what I've got. I'm trying to use the DynamicParameters method (which may not be the correct thing to use) to perform the logic which figures out which implementation to use and then calling Resolve to pull out that specific version. But I do not know how to return the resolved object, since DynamicParameters is expecting a ComponentReleasingDelegate, which I assume means it should be something like return k => { k.ReleaseComponent(dataSource); }. But then how do I yield the dataSource I've acquired back to the container for it to return to the caller?
struct DataSourceInfo {
string Id;
string ProviderType;
}
interface ICatalog : IDictionary<string /* Id */, DataSourceInfo> {
/* ... */
}
class Catalog : ICatalog {
/* implement dictionary which looks up DataSourceInfo from their string id */
}
interface IDataSource { /* ... */ }
class Source1 : IDataSource {
Source1(string id, IPrincipal principal) { /* ... */ }
}
class Source2 : IDataSource {
Source2(string id, IPrincipal principal) { /* ... */ }
}
/* ... */
/* ... inside Windsor configuration section */
container.Register(Component.For<ICatalog>().LifeStyle.Singleton.ImplementedBy<Catalog>());
// Default service provider is a factory method which uses the string (data source id)
// and looks up the DataSourceInfo from the ICatalog. It then uses info.ProviderType
// to request IoC to resolve that specific implementation and passes in "id" and "principal"
// to be used to resolve the dependencies of the implementation
container.Register(Component.For<IDataSource>().LifeStyle.Transient
.DynamicParameters((kernel, context, args) => {
if (args == null || !args.Contains("id") || !(args["id"] is string)) throw ApplicationException("bad args");
var id = (string)args["id"];
var catalog = kernel.Resolve<ICatalog>();
DataSourceInfo info;
try { info = catalog[id]; } finally { kernel.ReleaseComponent(catalog); }
// Now resolve the actual IDataSource
var dataSource = kernel.Resolve<IDataSource>(info.ProviderType, args);
// How do I return dataSource???
});
// Now register the actual implementations
container.Register(Component.For<IDataSource>().LifeStyle.Transient.ImplementedBy<Source1>().Named("Source1"));
container.Register(Component.For<IDataSource>().LifeStyle.Transient.ImplementedBy<Source2>().Named("Source2"));
/* ... */
/* some application startup code which configures some data sources */
class AppConfigurer {
AppConfigurer(ICatalog catalog) {
catalog["sourceA"] = new DataSourceInfo() { Id = "sourceA", ProviderType = "Source1" }; // data sourceA is provided by Source1 class
catalog["sourceB"] = new DataSourceInfo() { Id = "sourceB", ProviderType = "Source2" }; // data sourceB is provided by Source2 class
catalog["sourceC"] = new DataSourceInfo() { Id = "sourceC", ProviderType = "Source2" }; // data sourceC is provided by Source2 class
catalog["sourceD"] = new DataSourceInfo() { Id = "sourceD", ProviderType = "Source2" }; // data sourceD is provided by Source2 class
catalog["sourceE"] = new DataSourceInfo() { Id = "sourceE", ProviderType = "Source1" }; // data sourceE is provided by Source1 class
}
}
// Here is where I actually want to use IDataSources, and I do not want to know all the business about IDataSourceInfo. I just know a dataSourceId and an IPrincipal and want to get an IDataSource to work with.
class Dependant {
Dependant (Func<string, IPrincipal, IDataSource> factory) {
var sourceA = factory("sourceA", somePrincipal); // sourceA.GetType() == typeof(Source1)
var sourceB = factory("sourceB", somePrincipal); // sourceB.GetType() == typeof(Source2)
var sourceC = factory("sourceC", somePrincipal); // sourceC.GetType() == typeof(Source2)
}
}
Edit: by switching from DynamicParameters to UsingFactoryMethod I am able to do what I want. But I keep thinking this is wrong because now if I do container.ResolveAll() I'd really like it to skip the factory method but I don't know how to make it do that.
Why not just create a custom component type selector and decide which component to load based on that?
Typed Factory Facility - interface-based factories

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.