I need to dynamically build a object that I can then serialize to a JSON string. Essentially I'm working off of two Dictionaries that I need to use to create the new object.
var myValues= new Dictionary<string, object>
{
{ "Value1", "Foo" },
{ "Value2", "Bar" }
};
var mappedValues = new Dictionary<string, string>
{
{ "Value1", "Some:Path" },
{ "Value2", "Some:OtherPath }
};
As I loop through I need to be able to build out the Json Object so that I end up with something like:
{
"Some": {
"Path": "Foo",
"OtherPath": "Bar"
}
}
From what I've seen there is no way built into Newtonsoft.Json to do this specifically, but I'm hoping that someone may have an idea of how I might be able to most efficiently accomplish the goal.
It doesn't seem that there is any sort of way to create an element built into Newtonsoft.Json at this time... however I eventually came up with the following solution which works
private JObject DoFoo()
{
var jObject = new JObject();
foreach(var mapping in mappedValues)
{
var queue = new Queue<string>(mapping.Value.Split(':');
var value = myValues[mapping.Key];
SetValueWithPath(jObject, queue, value);
}
return jObject;
}
private void SetValueWithPath(JObject parent, Queue<string> path, object content)
{
var currentNode = path?.FirstOrDefault();
if (string.IsNullOrEmpty(currentNode)) return;
if (path.Count == 1)
{
parent[currentNode] = JToken.FromObject(content);
return;
}
else if (!parent.ContainsKey(currentNode))
{
parent[currentNode] = new JObject();
}
path.Dequeue();
SetValueWithPath(parent[currentNode] as JObject, path, content);
}
Related
If I am sending JSON data (via POST) to a .Net Core Web API like this
{ a: "a", b: "b" }
What do I need to do to have a controller method like this?
[HttpPost]
public async Task SometMethod(string a, string b)
{
return Ok();
}
Normally, all tutorials and docs say that you need to define a class and use [FromBody] attribute. But how can I make do without extra classes that I don't really need?
Firstly,your json should be:
{
"a":"a",
"b":"b"
}
You could receive data as JObject instead of a class like below:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpPost]
public void Post(JObject data)
{
//get the property value like below
var data1 = data["a"].ToString();
var data2 = data["b"].ToString();
}
}
Result (For easily distinguish value and property name,I change a to aaa and b to bbb):
If you want to post the data to the method like this, you will have to serialize your data before you can send it to the server. Assuming you are using JQuery, you can do like the following.
var postData = $.param({ a: "a", b: "b" });
//Then you can send this postData obejct to the server. This should perfectly bound to the parameters.
You can also use the same in an angular app.
After some research I came up with ModelBinder to do just this. It is not performant since it re-parses the whole request body for every parameter. I will improve it in the future.
https://github.com/egorpavlikhin/JsonParametersModelBinder
public class JsonBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
var actionDescriptor = bindingContext.ActionContext.ActionDescriptor as ControllerActionDescriptor;
if (actionDescriptor.MethodInfo.GetCustomAttributes(typeof(JsonParametersAttribute), false).Length > 0)
{
var context = bindingContext.HttpContext;
if (context.Request.ContentType != "application/json")
{
bindingContext.Result = ModelBindingResult.Failed();
return;
}
#if (NETSTANDARD2_1 || NETCOREAPP3_0)
context?.Request.EnableBuffering();
#else
context?.Request.EnableRewind();
#endif
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8,
false,
1024,
true); // so body can be re-read next time
var body = await reader.ReadToEndAsync();
var json = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(body);
if (json.TryGetValue(bindingContext.FieldName, out var value))
{
if (bindingContext.ModelType == typeof(string))
{
bindingContext.Result = ModelBindingResult.Success(value.GetString());
}
else if (bindingContext.ModelType == typeof(object))
{
var serializerOptions = new JsonSerializerOptions
{
Converters = {new DynamicJsonConverter()}
};
var val = JsonSerializer.Deserialize<dynamic>(value.ToString(), serializerOptions);
bindingContext.Result = ModelBindingResult.Success(val);
}
}
context.Request.Body.Position = 0; // rewind
}
}
}
If I have this controller:
public class SomeController : ApiController
{
public SomeInfoDto Get()
{
return new SomeInfoDto();
}
}
When I call /api/Some with a get request I will get JSON in the Content Body because JSON.NET has been kind enough to serialise it for me.
However, if I wanted to send a 500 HTTP code and some JSON I thought I could do this:
public HttpResponseMessage Get()
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = // not sure what to put here
}
}
So I googled to find out what to put for the Content and found this resource
public class JsonContent : HttpContent
{
private readonly JToken _value;
public JsonContent(JToken value)
{
_value = value;
Headers.ContentType = new MediaTypeHeaderValue("application/json");
}
protected override Task SerializeToStreamAsync(Stream stream,
TransportContext context)
{
var jw = new JsonTextWriter(new StreamWriter(stream))
{
Formatting = Formatting.Indented
};
_value.WriteTo(jw);
jw.Flush();
return Task.FromResult<object>(null);
}
protected override bool TryComputeLength(out long length)
{
length = -1;
return false;
}
}
Which I can easily modify to fit my purposes.
However, my question is, if I get SerializeToStreamAsync to use JsonConvert.SerializeObject(_value) further down the Web Api pipeline will it be serialised again?
I have set the GlobalConfiguration.Configuration like this:
var formatters = GlobalConfiguration.Configuration.Formatters;
var jsonFormatter = formatters.JsonFormatter;
var settings = jsonFormatter.SerializerSettings;
settings.Formatting = Formatting.Indented;
settings.ContractResolver = new CamelCasePropertyNamesContractResolver();
My Implementation for the SerializeToStreamAsync is this:
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
using (var streamWriter = new StreamWriter(stream))
{
streamWriter.WriteAsync(JsonConvert.SerializeObject(this.value)).Wait();
streamWriter.FlushAsync().Wait();
}
return Task.FromResult<object>(null);
}
EDIT: Providing a concrete sample with formatter.
If you want to send JSON content with Http Status Code 500, you can use the extension method HttpRequestMessage.CreateResponse. There is no need for any advanced serialization and formatting. More info on that here.
https://msdn.microsoft.com/en-us/library/system.net.http.httprequestmessageextensions.createresponse%28v=vs.118%29.aspx
Request.CreateResponse(HttpStatusCode.InternalServerError, new string[] { "value1", "value2" }, new JsonMediaTypeFormatter())
(Or)
Request.CreateResponse(HttpStatusCode.InternalServerError, new string[] { "value1", "value2" }, 'application/json')
I want to automate the Setup code by a given array containing expected result and parameters.
Something like data driven setup.
My existing code:
var mock = new Mock<ILogin>();
var testDataTable = new object[,]
{
{ LoginResult.Success, "Jack", "123!##"}
, { LoginResult.WrongPassword, "Jack", "123321"}
, { LoginResult.NoSuchUser, "Peter", "123!##"}
};
// ForEachRow is my own extension method
testDataTable.ForEachRow((row) =>
{
var result = (LoginResult)row[0];
var username = (string)row[1];
var password = (string)row[2];
mock.Setup(o => o.Login(
It.Is<string>(u => u == username),
It.Is<string>(p => p == password)
)).Returns(result);
});
return mock.Object;
Code that I wish:
var mock = new Mock<ILogin>();
new object[,]
{
{ LoginResult.Success, "Jack", "123!##"}
, { LoginResult.WrongPassword, "Jack", "123321"}
, { LoginResult.NoSuchUser, "Peter", "123!##"}
}.ForEachRow((row) =>
{
var exprTree = (ILogin o)=>o.Login("ANY", "ANY");
AutoSetup(mock, exprTree, row); // <---- How to write this AutoSetup?
});
return mock.Object;
How to write the AutoSetup(mock, exprTree, dataArray) function above?
It takes three parameters:
mock: A mock object, e.g. new Mock()
exprTree: Expression tree that represents a method to be setup
dataArray: An object[], the 0 element is the expected result, and others are parameters that passed to the method
This was an interesting challenge. I think I have a working implementation for your AutoSetup method, using the expressions API. If anyone has a simpler solution, I'd love to see it.
static void AutoSetup<TMock, TResult>(Mock<TMock> mock, Expression<Func<TMock, TResult>> exprTree, object[] items) where TMock : class
{
var methodCallExpr = exprTree.Body as MethodCallExpression;
var arguments = items.Skip(1).Select(o => Expression.Constant(o));
var updatedMethod = methodCallExpr.Update(methodCallExpr.Object, arguments);
var updatedLambda = exprTree.Update(updatedMethod, exprTree.Parameters);
mock.Setup(updatedLambda).Returns((TResult)items[0]);
}
Here is a full working test as a console app, with an implementation of ForEachRow, which you didn't provide.
class Program
{
static void Main(string[] args)
{
var login = SetUp();
Console.WriteLine(login.Login("Jack", "123!##"));
Console.WriteLine(login.Login("Jack", "123321"));
Console.WriteLine(login.Login("Peter", "123!##"));
Console.ReadKey();
}
static ILogin SetUp()
{
var mock = new Mock<ILogin>(MockBehavior.Strict);
var rows = new object[,]
{
{ LoginResult.Success, "Jack", "123!##" },
{ LoginResult.WrongPassword, "Jack", "123321" },
{ LoginResult.NoSuchUser, "Peter", "123!##" }
};
rows.ForEachRow((row) => AutoSetup(mock, (ILogin l) => l.Login("ANY", "ANY"), row));
return mock.Object;
}
private static void AutoSetup<TMock, TResult>(Mock<TMock> mock, Expression<Func<TMock, TResult>> exprTree, object[] items) where TMock : class
{
var methodCallExpr = exprTree.Body as MethodCallExpression;
var arguments = items.Skip(1).Select(o => Expression.Constant(o));
var updatedMethod = methodCallExpr.Update(methodCallExpr.Object, arguments);
var updatedLambda = exprTree.Update(updatedMethod, exprTree.Parameters);
mock.Setup(updatedLambda).Returns((TResult)items[0]);
}
}
public static class ArrayExtensions
{
public static void ForEachRow<T>(this T[,] rows, Action<T[]> action)
{
var x = rows.GetLength(1);
var y = rows.GetLength(0);
for (int i = 0; i < y; i++)
{
var row = new T[x];
for (int j = 0; j < x; j++)
{
row[j] = rows[i, j];
}
action(row);
}
}
}
public interface ILogin
{
LoginResult Login(string p1, string p2);
}
public enum LoginResult
{
Success,
WrongPassword,
NoSuchUser
}
EDIT: You asked in a comment about how to take advantage of the variable parameter matching that Moq provides with the It.IsAny<> method. Because what you pass to your Mock.Setup() is an expression tree, it's able to scan the method parameters and implement special behaviour for any that are calls to It.IsAny<>. However, if you use It.IsAny<> in your test data array, by the time we retrieve it from your items array to set in on the expression, it's not a method call but simply the result of the call it It.IsAny<> which is default(T) (see here).
We need some way of specifying in your test data array that the parameter should be any. Then we can check for this special case and generate the correct MethodCallExpression representing a call to It.IsAny<>.
Here are the changes I made to support this:
Add an Any type to use in your test data
public class Any<T>
{
private Any() { }
public static Any<T> Param { get { return new Any<T>(); } }
}
Update the AutoSetup method to handle this special case:
private static void AutoSetup<TMock, TResult>(Mock<TMock> mock, Expression<Func<TMock, TResult>> exprTree, object[] items) where TMock : class
{
var arguments = items.Skip(1).Select(o => {
var type = o.GetType();
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Any<>))
{
var typeParameter = type.GetGenericArguments();
var genericItIsAny = typeof(It).GetMethod("IsAny");
var itIsAny = genericItIsAny.MakeGenericMethod(typeParameter);
return Expression.Call(itIsAny) as Expression;
}
return Expression.Constant(o);
});
var methodCallExpr = exprTree.Body as MethodCallExpression;
var updatedMethod = methodCallExpr.Update(methodCallExpr.Object, arguments);
var updatedLambda = exprTree.Update(updatedMethod, exprTree.Parameters);
mock.Setup(updatedLambda).Returns((TResult)items[0]);
}
In the test data, use the Any type
{ LoginResult.Success, "NoPasswordUser", Any<string>.Param }
Depending on how you're storing your test data, you might need another way of identifying parameters that you want to be variable (specially formatted string?), but in general this should give you an idea about how to create the correct expressions.
Consider following code:
My problem is:
1) I can't seem to cast the errors to HttpContent
2) I can't use the CreateContent extension method as this doesn't exist on the context.Response.Content.CreateContent
The example here only seems to provide StringContent and I'd like to be able to pass the content as a JsobObject:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
public class ServiceLayerExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
var exception = context.Exception as ModelValidationException;
if ( exception != null )
{
var modelState = new ModelStateDictionary();
modelState.AddModelError(exception.Key, exception.Description);
var errors = modelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage);
// Cannot cast errors to HttpContent??
// var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = errors};
// throw new HttpResponseException(resp);
// Cannot create response from extension method??
//context.Response.Content.CreateContent
}
else
{
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
}
}
base.OnException(context);
}
}
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
context.Response.Content = new StringContent("Hello World");
you also have the possibility to use the CreateResponse (added in RC to replace the generic HttpResponseMessage<T> class that no longer exists) method if you want to pass complex objects:
context.Response = context.Request.CreateResponse(
context.Exception.ConvertToHttpStatus(),
new MyViewModel { Foo = "bar" }
);
I am using ASP.NET MVC 3.
Can someone please help me clarify what's happening here:
var person = new PersonRepository().Get();
var personViewModel = new PersonViewModel();
personViewModel.InjectFrom<LoopValueInjection>(person)
.InjectFrom<CountryToLookup>(person);
I have a grid on my Index view. Each row is an instance of a CategoryViewModel. So what I do is to get a list of all the categories and then map each Category to a CategoryViewModel, and then pass this list of CategoryViewModels to the view. Hou would I do a mapping like that?
IEnumerable<Category> categoryList = categoryService.GetAll();
I thought the following would work but it doesn't:
// Mapping
IList<CategoryViewModel> viewModelList = new List<CategoryViewModel>();
viewModelList.InjectFrom(categoryList);
AFAIK value injecter doesn't support automatic collection mapping like AutoMapper but you could use a simple LINQ expression and operate on each element:
IEnumerable<Category> categoryList = categoryService.GetAll();
IList<CategoryViewModel> viewModelList = categoryList
.Select(x => new CategoryViewModel().InjectFrom(x)).Cast<CategoryViewModel>()
.ToList();
//source list
IEnumerable<string> items = new string[] { "1", "2" };
// target list
List<int> converted = new List<int>();
// inject all
converted.InjectFrom(items);
And the extension method:
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, IEnumerable<TFrom> from) where TTo : new()
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
return to;
}
ICollection<T> is the interface that got least features but a Add method.
Update
An example using more proper models:
var persons = new PersonRepository().GetAll();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(persons);
Update - Inject from different sources
public static ICollection<TTo> InjectFrom<TFrom, TTo>(this ICollection<TTo> to, params IEnumerable<TFrom>[] sources) where TTo : new()
{
foreach (var from in sources)
{
foreach (var source in from)
{
var target = new TTo();
target.InjectFrom(source);
to.Add(target);
}
}
return to;
}
Usage:
var activeUsers = new PersonRepository().GetActive();
var lockedUsers = new PersonRepository().GetLocked();
var personViewModels = new List<PersonViewModel>();
personViewModels.InjectFrom(activeUsers, lockedUsers);
Use this function definition
public static object InjectCompleteFrom(this object target, object source)
{
if (target.GetType().IsGenericType &&
target.GetType().GetGenericTypeDefinition() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
target.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)) &&
source.GetType().IsGenericType &&
source.GetType().GetGenericTypeDefinition() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces() != null &&
source.GetType().GetGenericTypeDefinition().GetInterfaces()
.Contains(typeof(IEnumerable)))
{
var t = target.GetType().GetGenericArguments()[0];
var tlist = typeof(List<>).MakeGenericType(t);
var addMethod = tlist.GetMethod("Add");
foreach (var sourceItem in source as IEnumerable)
{
var e = Activator.CreateInstance(t).InjectFrom<CloneInjection>(sourceItem);
addMethod.Invoke(target, new[] { e });
}
return target;
}
else
{
return target.InjectFrom(source);
}
}
For those like me who prefer shortest notations possible
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, ICollection<TOrig> source) where TTarget : new()
{
source.Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e));
return target;
}
public static ICollection<TTarget> InjectFromList<TTarget, TOrig>(this ICollection<TTarget> target, params ICollection<TOrig>[] sources) where TTarget : new()
{
sources.ToList().ForEach(s => s.ToList().Select(r => new TTarget().InjectFrom(r))
.Cast<TTarget>().ToList().ForEach(e => target.Add(e)));
return target;
}
Create a generic list mapper:
public class ValueMapper
{
public static TResult Map<TResult>(object item) where TResult : class
{
return item == null ? null : Mapper.Map<TResult>(item);
}
public static IEnumerable<TResult> MapList<TResult>(IEnumerable<object> items) where TResult : class
{
return items?.Select(i => Mapper.Map<TResult>(i));
}
}
Now you can reference the ValueMapper class wherever you want, and call both Map and MapList
var mydtos = ValueMapper.MapList<MyDto>(dtos);
var mydto = ValueMapper.Map<MyDto>(dto);