This is with the Docusign Rest api. When I call ToJson() on an EnvelopeDefinition, it returns the correct info, but I would like it to not serialize the base64 array for when I am writing this out to a log file. I tried using the [JsonIgnore] directive, but that stopped the array from being serialized altogether. Do I need to override the Serialize method on this class or just create another method, something like ToJsonForLogging() and not serialize that array?
I have created an extension method that will work for you. You can call this extension method in your code as follows
string json = envelopeDefinition.ToJsonLog(logDocumentBase64:false)
I am copying the DocumentBase64 into a temporary List and then using .ToJson() function to log without the documentBase64 property.
public static class EnvelopeDefinitionExtensions
{
public static string ToJsonLog(this EnvelopeDefinition envDefinition, bool logDocumentBase64 = true)
{
if (logDocumentBase64) return envDefinition.ToJson();
var tempDocumentBase64List = new List<string>();
foreach(var doc in envDefinition.Documents)
{
tempDocumentBase64List.Add(doc.DocumentBase64);
doc.DocumentBase64 = null;
}
string json = envDefinition.ToJson();
int i =0;
foreach(var doc in envDefinition.Documents)
{
doc.DocumentBase64 = tempDocumentBase64List[i];
i++;
}
return json;
}
}
Related
I have a Controller that defines a number of Get methods. The Razor Component is able to read all the Gets using GetFromJsonAsync() without any issue. However, one of the Get methods returns a string object. The Razor Component keeps blowing up when trying to read that string.
MyController.cs
[HttpGet("GetObject")]
public async Task<MyObject> GetObject(int? id)
{
MyObject obj = new MyObject();
// ... do some work here and fill in obj
return obj;
}
[HttpGet("GetString")]
public async Task<string> GetString(int? id)
{
string retval = "";
// ... do some work here and fill in retval
return retval;
}
MyComponent.razor
// this call works
MyObject myObj = await _http.GetFromJsonAsync<string>("My/GetObject?id=15");
// this call throws a Json syntax error
string myString = await _http.GetFromJsonAsync<string>("My/GetString?id=15");
I'm not sure why the system is able to read all my objects with the exception of the string object. Has anyone else run into this issue with being unable to read strings from the Controller?
I finally figured out what is going on. It appears that if your Controller is returning a string object, Blazor is assuming that you have already converted your data into a true Json string and will not attempt to serialize it into Json for you. So I had to serialize it myself on the Controller side. Then my Razor Component picked up the original string as intended. Here is the solution:
[HttpGet("GetString")]
public async Task<string> GetString(int? id)
{
string retval = "";
// ... do some work here and fill in retval
return System.Text.Json.JsonSerializer.Serialize<string>(retval);
}
Input JSON:
{ "name": "gerry" }
Action method:
{ public ActionResult GenerateQrCode([FromBody] string name }
Problem:
The simple-type args are null
ModelState: Invalid
The built-in json deserializer can't handle the input in this form
I've tried:
ConfigureServices() -> services.AddControllersWithViews().AddNewtonsoftJson(); to switch to NewtonSoft, which I know/love
I've set a break-point into the non-NewtonSoft built-in MS SystemTextJsonInputFormatter.ctor() just to check, if it's still used: yes, it is, I'm not sure why, when I'm calling the above .AddNewtonsoftJson()
The situation:
The client POSTs all the input params as one JSON string document, which is UTF8 w/out BOM
The string comes in at the server-side and is nicely readable with new System.IO.StreamReader(Request.Body).ReadToEnd() from inside the immediate window
I need a way ASP.NET Core deserializes this, as it was able under the .NET4.X for many years w/out any issue
I wouldn't like to add [FromBody] and similar opt-in signatures all over the server actions/args
You pass the name as json but accept as a string so it will be null, you can use an InputFormatter like:
public class RawJsonBodyInputFormatter : InputFormatter
{
public RawJsonBodyInputFormatter()
{
this.SupportedMediaTypes.Add("application/json");
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
using (var reader = new StreamReader(request.Body))
{
var content = await reader.ReadToEndAsync();
return await InputFormatterResult.SuccessAsync(content);
}
}
protected override bool CanReadType(Type type)
{
return type == typeof(string);
}
}
In startup.cs:
services
.AddMvc(options =>
{
options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
});
And then you can get the row string
To deserilize it, you can check this, use Newtonsoft and make the string to a Model
[HttpPost]
public IActionResult GenerateQrCode([FromBody] string name)
{
object o = JsonConvert.DeserializeObject(name);
MyModel my = JsonConvert.DeserializeObject<MyModel>(o.ToString());
return View();
}
I have a method being posted to via AJAX with the following header:
public JsonResult GetDocuments(string searchTerm, SortRequest sort)
The SortRequest object is defined as follows:
[DataContract]
public class SortRequest
{
[DataMember(Name = "field")]
public string Field { get; set; }
[DataMember(Name = "dir")]
public string Direction { get; set; }
}
Because of legacy code, the JSON object has the property name "dir" which doesn't directly match the C# property name. We want to use Json.NET as the model binder for JSON requests because it is able to handle this, but the problem is that the JSON coming into the model binder looks like a single object with two top level properties, "searchTerm" and "sort". The deserialization process then tries to map that entire JSON string into each method parameter which obviously fails.
I have tried looking through the now open source .NET MVC code and have not yet been able to determine how the DefaultModelBinder class handles this gracefully. The only option I can see so far is to convert every JSON action to take in a single request parameter but this doesn't seem like a good solution as the DefaultModelBinder doesn't require this.
Edit for clarification:
The JSON request string looks something like this:
{
"searchTerm": "test",
"sort": {
"field": "name",
"dir": "asc"
}
}
We are overriding the DefaultModelBinder and only using Json.NET when the request is of type application/json. Here is the relevant code:
var request = controllerContext.HttpContext.Request;
request.InputStream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(request.InputStream))
{
var jsonString = reader.ReadToEnd();
result = JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType);
}
The bindingContext.ModelType is going to be set to String and SortRequest for each parameter in the method, but since the above is a single JSON object, it doesn't map to either of those types and thus inside the method itself, everything is set to default values.
I think the JsonProperty attribute can be used for this as follows:
[DataContract]
public class SortRequest
{
[DataMember(Name = "field")]
[JsonProperty("field")]
public string Field { get; set; }
[DataMember(Name = "dir")]
[JsonProperty("dir")]
public string Direction { get; set; }
}
Update
Based upon the json add a binding prefix:
public JsonResult GetDocuments(string searchTerm, [Bind(Prefix="sort"] SortRequest sort)
I ended up going with a solution using the JToken.Parse method in the Json.NET library. Essentially what is happening is that we check the top level properties of the JSON object and see if there exists the current action parameter we are trying to bind to. Where this falls down is if there is overlap between the parameter name of the action and a property name of a single request being passed in. I think this is enough of an edge case to let slide as it would require only a single object be passed into an action that is expecting multiple.
Here is the modified BindModel method:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object result;
if (IsJSONRequest(controllerContext))
{
var request = controllerContext.HttpContext.Request;
request.InputStream.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(request.InputStream))
{
var jsonString = reader.ReadToEnd();
// Only parse non-empty requests.
if (!String.IsNullOrWhiteSpace(jsonString))
{
// Parse the JSON into a generic key/value pair object.
var obj = JToken.Parse(jsonString);
// If the string parsed and there is a top level property of the same
// name as the parameter name we are looking for, use that property
// as the JSON object to de-serialize.
if (obj != null && obj.HasValues && obj[bindingContext.ModelName] != null)
{
jsonString = obj[bindingContext.ModelName].ToString();
}
}
result = JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType);
}
}
else
{
result = base.BindModel(controllerContext, bindingContext);
}
return result;
}
First off my question is very similar to below however I'm not sure if the answers are applicable to my specific problem or whether I just need clarification about how to approach it:
Convert LinkedHashMap<String,String> to an object in Java
I am using struts2 json rest plugin to convert a json array into a java array. The array is sent through an ajax post request and the java receives this data. However instead of being the object type I expect it is received as a LinkedHashmap. Which is identical to the json request in structure.
[
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true},
{advance_Or_Premium=10000, available=true}
]
The data is all present and correct but just in the wrong type. Ideally I want to send the data in my object type or if this is not possible convert the LinkedHashMap from a list of keys and values into the object array. Here is the class I am using, incoming data is received in the create() method:
#Namespace(value = "/rest")
public class OptionRequestAction extends MadeAbstractAction implements ModelDriven<ArrayList<OptionRequestRest>>
{
private String id;
ArrayList<OptionRequestRest> model = new ArrayList<OptionRequestRest>();
public HttpHeaders create()
{
// TODO - need to use model here but it's a LinkedHashmap
return new DefaultHttpHeaders("create");
}
public String getId()
{
return this.id;
}
public ArrayList<OptionRequestRest> getModel()
{
return this.model;
}
public ArrayList<OptionRequestRest> getOptionRequests()
{
#SuppressWarnings("unchecked")
ArrayList<OptionRequestRest> lReturn = (ArrayList<OptionRequestRest>) this.getSession().get("optionRequest");
return lReturn;
}
// Handles /option-request GET requests
public HttpHeaders index()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("index").lastModified(new Date());
}
public void setId(String pId)
{
this.id = pId;
}
public void setModel(ArrayList<OptionRequestRest> pModel)
{
this.model = pModel;
}
// Handles /option-request/{id} GET requests
public HttpHeaders show()
{
this.model = this.getOptionRequests();
return new DefaultHttpHeaders("show").lastModified(new Date());
}
}
One of the things which is confusing me is that this code works fine and returns the correct object type if the model is not an array. Please let me know if my question is not clear enough and needs additional information. Thanks.
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.