Como faço para enumerar por meio de um JObject?

111

Estou tentando determinar como acessar os dados que estão em meu JObject e não consigo determinar como usá-los.

JObject Object = (JObject)Response.Data["my_key"];

Posso imprimi-lo no console fazendo Console.WriteLine (Object) e vejo os dados, parece:

{
 "my_data" : "more of my string data"
...
}

Mas eu NÃO tenho ideia de como apenas iterar / enumerar através dele, alguém tem alguma ideia? Eu estou perdida agora.

Geesu
fonte

Respostas:

167

Se você olhar a documentação doJObject , verá que ele implementa IEnumerable<KeyValuePair<string, JToken>>. Portanto, você pode iterar sobre ele simplesmente usando um foreach:

foreach (var x in obj)
{
    string name = x.Key;
    JToken value = x.Value;
    
}
Svick
fonte
3
Isso está correto, mas por razões que não entendo, você não pode usá-lo com o Linq, a menos que lance explicitamente para o tipo enumerável. Ou seja, você em ((IEnumerable<KeyValuePair<string, JToken>>)obj).Select(...)vez do velho obj.Select(...); ou pelo menos é o que encontrei uma parte do meu código.
Adrian Ratnapala
2
@AdrianRatnapala Seu obj é declarado dinâmico? Métodos de extensão (como Enumerable.Select) não funcionam com isso.
Svick
1
Não, no meu caso objtinha tipo JObject; mas JObjectparece ter problemas semelhantes a dynamic. O compilador não pode inferir os argumentos de tipo para .Select. Posso explicá-los explicitamente, obj.Select<KeyValuePair<string, JToken>, (result type)>(...)também funciona para mim
Adrian Ratnapala
3
@AdrianRatnapala Hmm, você está certo. É porque JObjectimplementa ambos IEnumerable<KeyValuePair<string, JToken>>e IEnumerable<JToken>(indiretamente por meio de JContainer).
svick
3
Agora, como faço para usá-lo para JSON aninhado? Por exemplo, se o "valor" contém outro conjunto de pares chave: valor, como posso usar o JToken valuepara iterar no próximo conjunto de pares?
AlbatrossCafe
53

JObjects podem ser enumerados por meio de objetos JProperty lançando-os em um JToken :

foreach (JProperty x in (JToken)obj) { // if 'obj' is a JObject
    string name = x.Name;
    JToken value = x.Value;
}

Se você tiver um JObject aninhado dentro de outro JObject, não será necessário transmitir porque o acessador retornará um JToken:

foreach (JProperty x in obj["otherObject"]) { // Where 'obj' and 'obj["otherObject"]' are both JObjects
    string name = x.Name;
    JToken value = x.Value;
}
Daniel
fonte
3
Você também pode usar convenientemente linq assim: obj.Properties().Select(p => p.Name + ": " + p.Value).
itslittlejohn
Essa era exatamente a informação que eu procurava. Muito obrigado!
jhoepken
15

A resposta não funcionou para mim. Não sei como conseguiu tantos votos. Embora tenha ajudado a me apontar em uma direção.

Esta é a resposta que funcionou para mim:

foreach (var x in jobj)
{
    var key = ((JProperty) (x)).Name;
    var jvalue = ((JProperty)(x)).Value ;
}
Jaxxbo
fonte
1
Com isso (após esta linha: JObject priceComplianceJson = JObject.Parse (File.ReadAllText (fullPath));) eu obtenho, "Não é possível converter o tipo 'System.Collections.Generic.KeyValuePair <string, Newtonsoft.Json.Linq.JToken>' para 'Newtonsoft.Json.Linq.JProperty' "Porém, a remoção da fundição funciona: var key = x.Key; var jvalue = x.Value; - pelo menos ele compila ... Vou verificar a funcionalidade amanhã.
B. Clay Shannon
3

Para pessoas como eu, viciados em linq , e com base na resposta de svick , aqui está uma abordagem linq:

using System.Linq;
//...
//make it linq iterable. 
var obj_linq = Response.Cast<KeyValuePair<string, JToken>>();

Agora você pode fazer expressões linq como:

JToken x = obj_linq
          .Where( d => d.Key == "my_key")
          .Select(v => v)
          .FirstOrDefault()
          .Value;
string y = ((JValue)x).Value;

Ou apenas:

var y = obj_linq
       .Where(d => d.Key == "my_key")
       .Select(v => ((JValue)v.Value).Value)
       .FirstOrDefault();

Ou este para iterar sobre todos os dados:

obj_linq.ToList().ForEach( x => { do stuff } ); 
dani herrera
fonte