I'm using Auth0 for a new app I'm writing using ASP.NET Core 1.0. It's working great so far, but I have bumped into something that is stumping me for some reason.
When the user logs in, Auth0 will pass claims back to my app. Auth0 has the ability to add custom data to the user, it is stored in JSON format and comes over as a list of claims.
One of the claims will look something like this:
Type "app_metadata"
Value "\"IsPublisherFor\":[\"p56\",\"p124\",\"p258\"]"
ValueType "JSON"
My question is, how would I convert that claim value into something I can work with? For example, the ability to do something like IsPublisherFor.Contains("p56");
I tried to pass the value to NewtonSoft.Json.JsonConvert.DeserializeObject(value) but it throws an exception. JsonReaderException: Additional text encountered after finished reading JSON content
Any way to convert this cleanly?
You probably need to wrap your JSON string in curly braces. You generally need a single top-level array or object to have valid JSON. Don't know about NewtonSoft, but using the JavascriptSerializer, this works:
var json = "{\"IsPublisherFor\":[\"p56\",\"p124\",\"p258\"]}";
var serializer = new JavaScriptSerializer();
stuff obj = serializer.Deserialize<stuff>(json);
With the following class defined to receive the data:
public class stuff
{
public string[] IsPublisherFor { get; set; }
}
Related
I know you can do this easily with Newtonsoft. As I am working with .NET Core 3.0, however, I am trying to use the new methods for interacting with JSON files —i.e., System.Text.Json—and I refuse to believe that what I am trying to do is all that difficult!
My application needs to list users that have not already been added to my database. In order to get the full list of all users, the app retrieves a JSON string from a web API. I now need to cycle through each of these users and check if they have already been added to my application before returning a new JSON list to my view so that it can display the new potential users to the end user.
As I am ultimately returning another JSON at the end of the process, I don't especially want to bother deserializing it to a model. Note that the structure of data from the API could change, but it will always have a key from which I can compare to my database records.
My code currently looks like this:
using (WebClient wc = new WebClient())
{
var rawJsonDownload = wc.DownloadString("WEB API CALL");
var users = JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);
foreach (var user in users.ToList())
{
//Check if User is new
if (CHECKS)
{
users.Remove(user);
}
}
return Json(users);
}
This seems like a lot of hoops to jump through in order to achieve something that would be fairly trivial with Newtonsoft.
Can anyone advise me on a better way of doing this—and, ideally, without the need for the UserObject?
Your problem is that you would like to retrieve, filter, and pass along some JSON without needing to define a complete data model for that JSON. With Json.NET, you could use LINQ to JSON for this purpose. Your question is, can this currently be solved as easily with System.Text.Json?
As of .NET 6, this cannot be done quite as easily with System.Text.Json because it has no support for JSONPath which is often quite convenient in such applications. There is currently an open issue Add JsonPath support to JsonDocument/JsonElement #41537 tracking this.
That being said, imagine you have the following JSON:
[
{
"id": 1,
"name": "name 1",
"address": {
"Line1": "line 1",
"Line2": "line 2"
},
// More properties omitted
}
//, Other array entries omitted
]
And some Predicate<long> shouldSkip filter method indicating whether an entry with a specific id should not be returned, corresponding to CHECKS in your question. What are your options?
In .NET 6 and later you could parse your JSON to a JsonNode, edit its contents, and return the modified JSON. A JsonNode represents an editable JSON Document Object Model and thus most closely corresponds to Newtonsoft's JToken hierarchy.
The following code shows an example of this:
var root = JsonNode.Parse(rawJsonDownload).AsArray(); // AsArray() throws if the root node is not an array.
for (int i = root.Count - 1; i >= 0; i--)
{
if (shouldSkip(root[i].AsObject()["id"].GetValue<long>()))
root.RemoveAt(i);
}
return Json(root);
Mockup fiddle #1 here
In .NET Core 3.x and later, you could parse to a JsonDocument and return some filtered set of JsonElement nodes. This works well if the filtering logic is very simple and you don't need to modify the JSON in any other way. But do note the following limitations of JsonDocument:
JsonDocument and JsonElement are read-only. They can be used only to examine JSON values, not to modify or create JSON values.
JsonDocument is disposable, and in fact must needs be disposed to minimize the impact of the garbage collector (GC) in high-usage scenarios, according to the docs. In order to return a JsonElement you must clone it.
The filtering scenario in the question is simple enough that the following code can be used:
using var usersDocument = JsonDocument.Parse(rawJsonDownload);
var users = usersDocument.RootElement.EnumerateArray()
.Where(e => !shouldSkip(e.GetProperty("id").GetInt64()))
.Select(e => e.Clone())
.ToList();
return Json(users);
Mockup fiddle #2 here.
In any version, you could create a partial data model that deserializes only the properties you need for filtering, with the remaining JSON bound to a [JsonExtensionDataAttribute] property. This should allow you to implement the necessary filtering without needing to hardcode an entire data model.
To do this, define the following model:
public class UserObject
{
[JsonPropertyName("id")]
public long Id { get; set; }
[System.Text.Json.Serialization.JsonExtensionDataAttribute]
public IDictionary<string, object> ExtensionData { get; set; }
}
And deserialize and filter as follows:
var users = JsonSerializer.Deserialize<List<UserObject>>(rawJsonDownload);
users.RemoveAll(u => shouldSkip(u.Id));
return Json(users);
This approach ensures that properties relevant to filtering can be deserialized appropriately without needing to make any assumptions about the remainder of the JSON. While this isn't as quite easy as using LINQ to JSON, the total code complexity is bounded by the complexity of the filtering checks, not the complexity of the JSON. And in fact my opinion is that this approach is, in practice, a little easier to work with than the JsonDocument approach because it makes it somewhat easier to inject modifications to the JSON if required later.
Mockup fiddle #3 here.
No matter which you choose, you might consider ditching WebClient for HttpClient and using async deserialization. E.g.:
var httpClient = new HttpClient(); // Cache statically and reuse in production
var root = await httpClient.GetFromJsonAsync<JsonArray>("WEB API CALL");
Or
using var usersDocument = await JsonDocument.ParseAsync(await httpClient.GetStreamAsync("WEB API CALL"));
Or
var users = await JsonSerializer.DeserializeAsync<List<UserObject>>(await httpClient.GetStreamAsync("WEB API CALL"));
You would need to convert your API method to be async as well.
I'm wrapping a third-party API in a controller (due to security hoops we have to jump through) and the API returns a JSON string.
I do some minor changes to the JSON string and want to return that string.
I don't see a way to do that with a JSONResult, as it requires an object, and returning JSON string is sent back as a string.
Am I stuck with using something like a ContentResult?
JSONLint.com says the modified JSON is valid. It starts with...
[{"Acknowledgment Start Date":null,"Additional Location Details":null,"Area Code":null,"Assign To Vendor":"No",...
If I use the Newtonsoft.Json.JsonConvert(), it does this to my JSON string...
[[[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],...
If I use JavaScriptSerializer, I get this again...
[[[[]],[[]],[[]],[[]],[[]],[[]],[[]],[[]],...
I suspect part of the problem is the null values in the JSON string.
Is there another solution? Are there issues with using a ContentResult that I'm not aware of?
Newtonsoft.Json.JsonConvert() serializes an object, it doesn't parse (AKA deserialize) it. What you're doing when you call Newtonsoft.Json.JsonConvert(jsonString) is you're saying "serialize this JSON string to JSON". So, you get a funky result.
You could instead parse the JSON, then make your modifications, then serialize it again. For example:
var myObject = Newtonsoft.Json.DeserializeObject<POCOClass>(jsonString);
myObject.Whatever = "123";
//... etc.
This of course after defining your POCO class like such:
public class POCOClass {
[JsonProperty(PropertyName = "Acknowledgment Start Date")]
public string AcknowledgmentStartDate { get; set; }
// etc.
}
Then when you're done, serialize it back:
jsonString = Newtonsoft.Json.JsonConvert(myObject) // or Newtonsoft.Json.SerializeObject(myObject)
Upon closer inspection, I had a couple of realizations.
The first being that the outputted JSON isn't valid because the quoted identifiers include spaces and colons. For example, this one from above.
... ,"Additional Location Details":null, ...
While I suspect this is the root of problems, I don't have the time to write a parser to make it proper JSON.
I will come back and write a tool to properly clean up the mess at some point, but right now I have another approach I'm doing.
I need a ASP.NET MVC controller, which receives anonymous object from JS in JSON to iterate thru its properties. I used to do this, receiving Dictionary<string, object>. But now one of values is Array, and insted of
receivedDictionary[Photos] = [object, object, object]
it gets it as
receivedDictionary[Photos[0]] = object, receivedDictionary[Photos[1]] = object, receivedDictionary[Photos[2]] = object
I get not one dictionary entry with key = Photos and value = array, but many entries with key = Photos[x] and value = object.
How do I get it as one entry in dictionary or is there any better way to get it as dynamic anonymous object and iterate thru its properties just like in JS?
UPD: JSON looks like this:
{"fields":{"TotalFloors":"9","HouseNumber":"10","Photos":[{"id":0,"ParentID":0,"OriginalUrl":"py4s1y3uyqu","OriginalExt":".jpg","ThumbUrl":"2hn04w2lzuu","FormatUrls":{"WH_109_82_Url":"4cwjarqudvo","WH_766_454_Url":"oofm5qo21rr"}},{"id":0,"ParentID":0,"OriginalUrl":"t3csgq20iro","OriginalExt":".jpg","ThumbUrl":"j1uwwburmse","FormatUrls":{"WH_109_82_Url":"gm4qoery1u2","WH_766_454_Url":"a3c20re3g1d"}}],"Details":"Other details"}}
Controller definition:
[HttpPut]
public ActionResult restId(string className, int id, Dictionary<string, object> fields)
{
....
}
The JsonValueProvider used by the DefaultModelBinder seems to be treating array in an odd fashion in this case (based on the source here), so even a dynamic will most likely have the issue. (Don't think you'll have this issue in MVC 6 however)
However, calling the JavascriptSerializer directly (which funny enough is what the default provider uses) produces the results you're after:
var js = new JavaScriptSerializer();
var res = js.DeserializeObject(#"{'TotalFloors':'9','HouseNumber':'10','Photos':[...],'Details':'Other details'}");
To address your issue you could either alter the parameter to a string and run the above code in your action (obviously replacing the JSON string with the parameter), just means the JSON you're submitting from the front end would need to look more like:
// wrapping the JSON object in quotes to stringify it
{ 'fields' : "{ 'TotalFloors': '9', 'HouseNumber': .... }" }
Otherwise you could implement a custom JsonValueProvider like the one proposed here: https://json.codeplex.com/discussions/347099
The custom value provider is probably the cleaner solution.
My Problem is that when I browse to the service URL I see "key-value" pairs but I don't see the name of the array or object, I need the name because I want to use that service for android.
it looks like this:
My code looks like this:
1. In ValueController I have method:
[AcceptVerbs("GET", "POST")]
public List<BuzzMonitor.Web.Message> Search(string text, DateTime dateFrom, DateTime dateTo, [ModelBinder]List<int> themeIds, [ModelBinder]List<int> sourceIds)
{
MessageHandler mh = new MessageHandler();
List<BuzzMonitor.Web.Message> messages = null;
messages = mh.Search(text,dateFrom,dateTo,themeIds,sourceIds);
return messages;
}
2. In Global.asax I added:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
Does anyone have an idea on what is missing when I don't see full JSON, and see just key:value pairs? Thank you for help!
I think you are thinking that you are not seeing the array name or class right? and that is an error?
The answer is NO.
Reason? Well, JSON was invented to share objects easily over network and the main goal was to make it independent of the underlying architecture -
http://www.json.org/
that is why you don't see array names or variable names, only the object notation. Thats JSON stands for Java Script Object Notation. It's the responsibility of the receiving side to re-construct the object from the data provided in json format.
in your case messages is an array with a list of data and so does the output -
[] means array and {} inside that means it has only one object in it.
EDIT: You can also use this to parse json -
http://developer.android.com/reference/org/json/JSONTokener.html
I believe that my .NET 4.5 DateTime object is being incorrectly serialized by its ApiController in my ASP.NET MCV 4 web app. After retrieving a DataRow object from an SQL Server database I assign it to a DateTime member object in my CarrierObject.
StartTime = (DateTime)row["start_time"]
After this I add the CarrierObject to a list and return the list when all relevant entries have been processed. The ApiController converts the list to a JSON string for transmission.
When I perform the API call on the client and read the JSON output I get output in the form of 2013-06-03T22:49:21.66
While this looks fine at first glance, attempting to parse this on the client side with
var client = new HttpClient();
var uri = new Uri("http://555.55.55.55/test4/api/values");
Stream respStream = await client.GetStreamAsync(uri);
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(List<CarrierObject>));
List<CarrierObject> feeds = (List<CarrierObject>)ser.ReadObject(respStream);
it fails with the exception
System.Runtime.Serialization.SerializationException was unhandled by user code
HResult=-2146233076
Message=There was an error deserializing the object of type System.Collections.Generic.List ... PublicKeyToken=null]]. DateTime content '2013-06-03T22:49:21' does not start with '\/Date(' and end with ')\/' as required for JSON.
It seems to be wanting something in the format /Date(1224043200000)/. While I could manually write a converter to produce the correct JSON format that would seem to defeat the purpose of the automatic serialization performed by the API controller and automatic deserialization performed by the DataContractJsonSerializer. Is there a way to make the DataContractJsonSerializer accept the format being produced by the API Controller or the opposite? Is there a different parser that I should be using?
Thank you for your time,
-- Techrocket9
Date serialization has always been a rough point for DataContractJsonSerializer. Try JSON.NET instead.
This is one of the reasons that WebAPI decided to use JSON.NET by default, rather than the older DataContractJsonSerializer.
Looks like it's really a gotcha in DataContractJsonSerializer. You can try to implement what this guy did here. Or this answer for a similar question raised before.