I've been browsing various websites and the JSON.net docs but I can't find an elegant way to do this.
The Problem
I have to parse a list of GitHub commits since a certain date.
The example json file I've been using for testing: example json file
The json is just a large (or empty) array. The problem is I don't need all of the data, I just need the sha of each commit.
However, if you look at each type in the array, there are multiple shas.
There is the base sha:
"sha": "fde139ae1d8fcf82bb145bbc99ed41763202e28f",
the tree sha:
"tree": {
"sha": "5d33d345f2df166bc4c56cc9307a61a5ee57d346",
"url": "https://api.github.com/repos/QuiZr/ProjectPorcupineLocalization/git/trees/5d33d345f2df166bc4c56cc9307a61a5ee57d346"
},
and the parent sha(s):
"parents": [
{
"sha": "8b9b43e813645c3a66911247b3dca916af937738",
"url": "https://api.github.com/repos/QuiZr/ProjectPorcupineLocalization/commits/8b9b43e813645c3a66911247b3dca916af937738",
"html_url": "https://github.com/QuiZr/ProjectPorcupineLocalization/commit/8b9b43e813645c3a66911247b3dca916af937738"
}
]
I only want the first sha. not the other shas.
Requirements
Needs to use JSON.net (not Ockokit)
Should be elegant
Needs to support the Unity game engine (i.e. C# version less than or equal to 2.0)
I really don't want to create a new object type just for this.
You can just use LINQ to JSON very easily in this case - parse the text as a JArray, then ask for the sha property of each object:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json.Linq;
class Test
{
static void Main()
{
string json = File.ReadAllText("test.json");
JArray array = JArray.Parse(json);
List<string> hashes = array.Select(o => (string) o["sha"]).ToList();
foreach (var hash in hashes)
{
Console.WriteLine(hash);
}
}
}
Note that this uses lambda expressions which are from C# 3, but that should be fine in Unity - it only supports the CLR v2, but the .NET 3.5 framework, I believe.
In future though, I wouldn't let concerns such as "It's only a small part of the codebase" put you off creating a type - if LINQ to JSON didn't exist, creating a model type for the commit and deserializing to that would be a perfectly good solution.
If you really do not want new type, you can deserialize it using anonymous types. http://www.newtonsoft.com/json/help/html/DeserializeAnonymousType.htm
Related
I'm trying to create a Json Parser for C# for use with Unity. Currently I'm using Json.net as the serializer and I'm having a bit of trouble. Essentially, in order to deserialize into a function, I'm using an Enum as a representation of a function pointer. the underlying code is working fine, but I'm having trouble getting the enum to map.
Essentially the object that would be created from the JSON string
"{'==': [1, 1]}"
would be
new Tuple<Enum, dynamic[]>(Enum.Equals, {1, 1})
and vice versa.
I have found this resource, but I don't think it goes quite far enough in its explanation: https://bytefish.de/blog/enums_json_net/
Essentially how do I set up Item 1 of the Tuple to be the name of Array Item 2?
One way to see JSON is as a combination of HashMaps (dictionaries), arrays and values.
When you parse a JSON into an object, that object is a combination of dictionaries, arrays, and values.
One way to represent an enum in JSON could be as follow:
{
"type": "string",
"enum: ["red", "blue", "yellow"]
}
So, perhaps you wish to extend the functionality of the library you are using to work with Tuples.
If I am not clear, please let me know.
I'm introducing Elasticsearch into a C# API project. I'd like to leverage existing API models as search documents, many of which allow for adding custom data points. These are implemented using the JObject type from Json.NET. For example:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public JObject ExtraProps { get; set; }
}
This allows users to send JSON request bodies like this, which works great:
{
"Id": 123,
"Name": "Thing",
"ExtraProps": {
"Color": "red",
"Size": "large"
}
}
However, if I use this as a document type in NEST, those extra properties are losing their values somehow, serializing as:
{
"Id": 123,
"Name": "Thing",
"ExtraProps": {
"Color": [],
"Size": []
}
}
Adding a [Nest.Object] attribute to ExtraProps didn't change the behavior. As I understand it, NEST uses Json.NET internally, so I wouldn't expect it to have problems with Json.NET types. Is there a relatively simple fix for this?
Here are some options I'm weighing:
Use custom serialization. I started down this path, it got to feeling way more complicated than it should be, and I never did get it working.
Map JObjects to Dictionary<string, object>s. I have verified this works, but if there are nested objects (which there could be), I'll need to enhance it with recursion. And, ideally, I'd like this to work with the more general JToken type. This is the option I'm leaning toward, but again, it feels more complicated than it should be.
Use the "Low Level" client or even raw HTTP calls. Admittedly I haven't explored this, but if it's really simpler/cleaner than the alternatives, I'm open to it.
Report this as a bug. I'll probably do this regardless. I just have a hunch this should work with JObject or any JToken out of the box, unless there is some reason that this is intended behavior.
This is expected behaviour with NEST 6.x.
NEST uses Json.NET for serialization. In NEST 6.x however, this dependency was internalized within the NEST assembly by
IL-merging all Json.NET types into the NEST assembly
renamespacing the types within Newtonsoft.Json to Nest.Json
marking all types internal
There's a blog post with further details explaining the motivations behind this change.
When it comes to handling Json.NET types such as Newtonsoft.Json.Linq.JObject, Json.NET has special handling for these types for serialization/deserialization. With NEST 6.x, the internalized Json.NET does not know how to specially handle Newtonsoft.Json.Linq.JObject because all types within the internalized Json.NET have been renamespaced to the Nest.Json namespace.
To support Json.NET types, a serializer that uses Json.NET to serialize your documents needs to be hooked up. The NEST.JsonNetSerializer nuget package was created to help with this. Simply add a reference to NEST.JsonNetSerializer to your project, then hook up the serializer as follows
// choose the appropriate IConnectionPool for your use case
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings =
new ConnectionSettings(pool, JsonNetSerializer.Default);
var client = new ElasticClient(connectionSettings);
With this is place, documents with JObject properties will be serialized as expected.
I'm attempting to read the contents of a JSON file using Newtonsoft JSON which is a list of dictionaries, iterate through them, and create a new list of dictionaries after rooting out the one I don't want which will eventually be written back to the JSON file.
No matter what I try, I can't seem to be able to add the JSON entries within its list back to a new List. Here's the error:
Unhandled Exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:
The best overloaded method match for 'System.Collections.Generic.List<System.Collections.Specialized.OrderedDictionary>.Add(System.Collections.Specialized.OrderedDictionary)'
has some invalid arguments
Here's the JSON string I've deserialized:
[
{
"name":"test",
"custom":false,
"file":"strawberry-perl-5.10.1.2portable.zip",
"url":"http://strawberryperl/....",
"ver":"5.10.1",
"csum":"f86ae4b14daf0b1162d2c4c90a9d22e4c2452a98"
}
]
And here's my code:
dynamic customPerlList = JsonParse("perls_custom");
List<dynamic> updatedList = new List<dynamic>();
foreach (var perlStruct in customPerlList)
{
if (perlStruct.name != perlVersionToRemove)
{
Console.WriteLine("match");
updatedList.Add((OrderedDictionary)perlStruct);
}
}
I've just started developing in C#, so my attempts using examples found while searching either aren't sinking in, or I'm missing something else. Can someone please point out the err in my ways, and what the proper approach is to do what I'm attempting?
The most likely problem is that typeless JSON objects typically are matched with IDictionary<string, object> interfaces in .NET libraries; OrderedDictionary does not have that interface. And really, JSON objects are not considered ordered.
Perhaps you can switch to using a regularDictionary<string, object>, or writing a specific class to serialize to/from.
If you wanted to use Dictionary<string, object>, then you should consider deserializing as follows:
var list = JsonConvert.ToObject<List<Dictionary<string, object>>>(s);
I've got c# objects that I need references to in javascript for default population. Currently I'm maintaining 2 different objects which is not that maintainable.
For example ( simplified for demo purposes ):
C#
public class Text
{
public string Name {get;set;}
}
JSON
{
'text': {
name: undefined
}
}
I know there is a number of ways to accomplish this but wondering if anyone has a recommended solution. Thanks!
I personally recommend json.NET. Getting the json of any object is as simple as;
using Newtonsoft.Json;
string json = JsonConvert.SerializeObject(new Text { Name = "test" });
There are a lot of other options but I've been using it since before there was json serilization support in .NET and I strongly prefer it over what is there now. In fact I think it's better in every way, if you want a big robust data layer I like it more and it's vastly superior for one off serilizations.
If you are using .NET 4.0 or above, you can use DataContractJsonSerializer class.
I recommend you to look at this benchmark http://theburningmonk.com/2013/09/binary-and-json-serializer-benchmarks-updated/
Something I'm confusing.
The Javascript is going to produce the following JSON data.
{type:"book" , author: "Lian", Publisher: "ABC"}
{type:"Newspaper", author: "Noke"}
This is only an example, actually I've got more than this.
Since I have common fields between different JSON data, so I don't know is it possible to pass this to C# at one time.
What I want to do is pass this to c# then do some processing, what is the best way to do? I'm using ASP.NET MVC2.
Thanks for your answer or hints.
The combination of the 2 JSON statements above are, together, not valid JSON. That being said, you will not be able to use the JavaScriptSerializer class to deserialize that data into c# structure directly. Instead you will have to do some manual parsing first, to either break it down into valid JSON or just do full on manual parsing.
What I would actually recommend is sending over valid JSON instead. You can accomplish this by doing something like this:
{list: [
{type:"book" , author: "Lian", Publisher: "ABC"},
{type:"Newspaper", author: "Noke"} ]
Hard to say exactly, since only you know the details of your use case. You can send this data over using a traditional 'ajax' request. This is very easy to do with out any of the many JS libraries out there, but I would recommend just going with one anyway - they offer higher level constructs that are easier to use (and address cross-browser idiosyncrasies).
Since you are using ASP.NET MVC2, I would recommend jQuery. Microsoft is now backing jQuery as their JS library of choice and even make it default for new web projects.
Once you pass the above JSON to C#, you can deserialize it by doing something like this:
JavaScriptSerializer serializer = new JavaScriptSerializer();
var result = serialzer.Deserialize<Dictionary<string, object>>(postedJSONData);
Your result will then have a structure that looks like this, in C#:
Dictionary<string, object> result =>
{ "list" => object },
object => List<object>,
List<object> => Dictionary<string, object>
{ "type" => "book", "author" => "Lian" } // etc
[
{type:"book" , author: "Lian", Publisher: "ABC"},
{type:"Newspaper", author: "Noke"}
]
Is still valid JSON (well actually keys need to be enclosed in " as well), so you can .push() into an array each time you create a JSON record.
var list = [];
// code doing other stuff
list.push({type:"book" , author: "Lian", Publisher: "ABC"});
// more code doing other stuff
list.push({type:"Newspaper", author: "Noke"})
Once your JSON list is constructed, you can send that list to the backend in one go.
You can also use the JavaScriptSerializer to deserialize your own custom type. Esentially you make a very simple type with all the properties of your json objects then call
JavaScriptSerializer serializer = new JavaScriptSerializer();
MyType result = serialzer.Deserialize<MyType>(JsonData);
You can also deserialize an array
MyType[] result = serialzer.Deserialize<MyType[]>(JsonData);