问题
I have the following json file:
{
"users": [
{
"tags": [
"c++",
"jquery",
"css",
"html"
],
"name": "John Doe",
"id": 0
},
{
"tags": [
"vb",
"css"
],
"name": "Bill Gates",
"id": 1
},
{
"tags": [
"c++",
"css",
"html"
],
"name": "Steve Jobs",
"id": 3
}
]
}
I'm trying to return only users who match Tags twice or more, for instance:
John Doe and Steve Jobs have c++ and css in common.
I was trying to achieve this by performing tons of for statements but I don't think this is the best solution.
That's what I have so far:
JObject res = JObject.Parse(File.ReadAllText(@"C:/json/data.json"));
int jsonLength = res["users"].Count();
for (int i = 0; i < jsonLength; i++)
{
string name = res["users"][i]["name"].ToString();
int id = Convert.ToInt32(res["users"][i]["id"]);
string tags = res["users"][i]["tags"].ToString();
Console.WriteLine("ID: " + id);
Console.WriteLine("Name: " + name);
Console.WriteLine("Tags: " + tags);
}
I saw some people querying a json file like they do in SQL but I've never used LINQ before so I have no idea on how it works also I'm not sure what's the best way to approach this.
回答1:
If you just want a list of all users that share two or more tags with another user, you could do something like this:
string json = File.ReadAllText(@"C:\json\data.json");
JArray users = (JArray)JObject.Parse(json)["users"];
// Generate pair-wise combinations of users
// and check for intersection of their tags.
// If two or more common tags, add both users to a hash set
HashSet<JObject> result = new HashSet<JObject>();
for (int i = 0; i < users.Count; i++)
{
JObject user1 = (JObject)users[i];
for (int j = i + 1; j < users.Count; j++)
{
JObject user2 = (JObject)users[j];
if (user1["tags"].Select(t => t.ToString())
.Intersect(user2["tags"].Select(t => t.ToString()))
.Count() > 1)
{
result.Add(user1);
result.Add(user2);
}
}
}
Console.WriteLine("All users that share two or more tags with another user:");
Console.WriteLine();
foreach (JObject user in result)
{
Console.WriteLine("id: " + user["id"]);
Console.WriteLine("name: " + user["name"]);
Console.WriteLine("tags: " + string.Join(", ", user["tags"]));
Console.WriteLine();
}
Fiddle: https://dotnetfiddle.net/IpZBIR
If you want to see each pair of users along with the common tags between them, you need a little more code to capture the pairings. First, I would create a couple of classes to make the data easier to work with:
class User
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> Tags { get; set; }
}
class Pairing
{
public User User1 { get; set; }
public User User2 { get; set; }
public List<string> CommonTags { get; set; }
}
Then you can capture the pairings like this:
string json = File.ReadAllText(@"C:\json\data.json");
// Parse the JSON into a list of Users
List<User> users = JObject.Parse(json)["users"]
.Select(t => t.ToObject<User>())
.ToList();
// Generate pair-wise combinations of users
// and check for intersection of their tags.
// If two or more common tags, add the pairing to a list
List<Pairing> pairings = new List<Pairing>();
for (int i = 0; i < users.Count; i++)
{
User user1 = users[i];
for (int j = i + 1; j < users.Count; j++)
{
User user2 = users[j];
var commonTags = user1.Tags.Intersect(user2.Tags).ToList();
if (commonTags.Count > 1)
{
pairings.Add(new Pairing
{
User1 = user1,
User2 = user2,
CommonTags = commonTags
});
}
}
}
// Write out the results
Console.WriteLine("Pairs of users sharing two or more tags with each other:");
Console.WriteLine();
foreach (Pairing p in pairings)
{
Console.WriteLine(string.Format("{0} (id {1}) and {2} (id {3}) have ({4}) in common.",
p.User1.Name, p.User1.Id, p.User2.Name, p.User2.Id, string.Join(", ", p.CommonTags)));
}
Fiddle: https://dotnetfiddle.net/vQlJSV
回答2:
You should create below model per your posted JSON (use http://json2csharp.com/). Deserialize your JSON string to RootObject
and then you can use LINQ query to filter the data per your requirement
public class User
{
public List<string> tags { get; set; }
public string name { get; set; }
public int id { get; set; }
}
public class RootObject
{
public List<User> users { get; set; }
}
回答3:
Create an User
class:
public class User
{
public IEnumerable<string> Tags { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
Then create a class to hold the JSON data:
public class ResponseData
{
public IEnumerable<User> Users { get; set; }
}
Now proceed to deserialize the JSON:
string json = "....";
ResponseData data = JsonConvert.DeserializeJson<ResponseData>(json);
IEnumerable<User> users = data.Users;
Then, to find users with a common tag, you can create an extension method:
public static IEnumerable<User> WithTag(this IEnumerable<User> users, string tag)
{
if (users == null) return null;
return users.Where(u => u.Tags.Contains(tag));
}
You would call the method like this:
IEnumerable<User> users = data.Users;
IEnumerable<User> cppGroup = users.WithTag("c++");
If you want to get all the possible tags:
public static IEnumerable<string> AllTags(this IEnumerable<User> users)
{
if (users == null) return null;
return users.Select(u => u.Tags).SelectMany(t => t).Distinct();
}
Finally, if you want to get all the common users for all tags:
public static IDictionary<string, IEnumerable<User>> AllCommonTags(this IEnumerable<User> users)
{
if (users == null) return null;
return users.AllTags().Select(t => new
{
Tag = t, Users = users.WithTag(t)
}).ToDictionary(ct => ct.Tag, ct => ct.Users);
}
You would consume it like this:
IEnumerable<User> users = data.Users;
IDictionary<string, IEnumerable<User>> commonTags = users.AllCommonTags();
IEnumerable<User> cppGroup = tags["c++"];
IEnumerable<User> htmlGroup = tags["html"];
来源:https://stackoverflow.com/questions/43687592/i-want-to-query-a-json-file-to-find-users-with-two-or-more-tags-in-common