Tengo el siguiente archivo json:

{
  "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
    }
  ]
}

Intento devolver solo los usuarios que coinciden con las etiquetas dos veces o más, por ejemplo:

John Doe y Steve Jobs tienen c ++ y css en común.

Estaba tratando de lograr esto realizando toneladas de declaraciones, pero no creo que esta sea la mejor solución.

Eso es lo que tengo hasta ahora:

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);
}

Vi a algunas personas consultar un archivo json como lo hacen en SQL, pero nunca he usado LINQ antes, así que no tengo idea de cómo funciona y tampoco estoy seguro de cuál es la mejor manera de abordar esto.

0
yaKay 28 abr. 2017 a las 22:27

3 respuestas

La mejor respuesta

Si solo desea una lista de todos los usuarios que comparten dos o más etiquetas con otro usuario, puede hacer algo como esto:

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();
}

Violín: https://dotnetfiddle.net/IpZBIR


Si desea ver cada par de usuarios junto con las etiquetas comunes entre ellos, necesita un poco más de código para capturar los emparejamientos. Primero, crearía un par de clases para facilitar el trabajo de los datos:

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; }
}

Entonces puedes capturar los emparejamientos así:

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
Brian Rogers 28 abr. 2017 a las 23:05

Debe crear el siguiente modelo según su JSON publicado (use http://json2csharp.com/). Deserialice su cadena JSON a RootObject y luego puede usar la consulta LINQ para filtrar los datos según sus requisitos

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; }
}
0
Rahul 28 abr. 2017 a las 19:33

Crea una clase User:

public class User
{
    public IEnumerable<string> Tags { get; set; }
    public string Name { get; set; }
    public int Id { get; set; }
}

Luego cree una clase para contener los datos JSON:

public class ResponseData
{
    public IEnumerable<User> Users { get; set; }
}

Ahora proceda a deserializar la JSON:

string json = "....";
ResponseData data = JsonConvert.DeserializeJson<ResponseData>(json);
IEnumerable<User> users = data.Users;

Luego, para encontrar usuarios con una etiqueta común, puede crear un método de extensión:

public static IEnumerable<User> WithTag(this IEnumerable<User> users, string tag)
{
     if (users == null) return null;
     return users.Where(u => u.Tags.Contains(tag));
}

Llamarías al método así:

IEnumerable<User> users = data.Users;
IEnumerable<User> cppGroup = users.WithTag("c++");

Si desea obtener todas las etiquetas posibles:

public static IEnumerable<string> AllTags(this IEnumerable<User> users)
{
    if (users == null) return null;
    return users.Select(u => u.Tags).SelectMany(t => t).Distinct();
}

Finalmente, si desea obtener todos los usuarios comunes para todas las etiquetas:

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);
}

Lo consumirías así:

IEnumerable<User> users = data.Users;
IDictionary<string, IEnumerable<User>> commonTags = users.AllCommonTags();

IEnumerable<User> cppGroup = tags["c++"];
IEnumerable<User> htmlGroup = tags["html"];
0
Matias Cicero 28 abr. 2017 a las 19:55