CodeGym /Cursos /C# SELF /Estruturas dinâmicas: JObj...

Estruturas dinâmicas: JObject, JArray

C# SELF
Nível 47, Lição 3
Disponível

1. Introdução

Nas palestras anteriores sempre trabalhamos com estruturas fortemente tipadas. Por exemplo, temos a classe Person, que serializamos para JSON e de volta:

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Mas às vezes você não sabe a estrutura dos dados de antemão. Por exemplo:

  • Você está escrevendo um parser para um serviço externo, onde a estrutura da resposta não é fixa.
  • Você precisa extrair só uma parte da informação, não necessariamente preencher uma classe inteira.
  • Precisa editar ou gerar JSON "on the fly", baseado em condições dinâmicas.

É aí que entram as "estruturas dinâmicas" — objetos que guardam a árvore JSON como um conjunto de chaves e valores, sem exigir uma classe C# descrita previamente.

Por que na maioria das vezes usam Newtonsoft.Json

No .NET há dois jogadores principais no campo de trabalho com JSON:

  • System.Text.Json — a biblioteca embutida da Microsoft (evoluindo desde o .NET Core 3.0).
  • Newtonsoft.Json (Json.NET) — a biblioteca popular que deu ao mundo C# classes como JObject e JArray.

No momento da escrita desta palestra, System.Text.Json ainda não oferece um análogo completo de JObject/JArray com a mesma conveniência. Então, se você precisa frequentemente analisar ou modificar estruturas JSON complexas/desconhecidas, costumam escolher Newtonsoft.Json.

Tipos principais: JObject, JArray, JValue e a família JToken

  • JToken — tipo base para todos os nós JSON.
  • JObject — objeto JSON { ... }, conjunto de pares "chave-valor".
  • JArray — array [ ... ].
  • JValue — um valor isolado (42, "texto", true, null).

A ideia é simples: parseamos o JSON — obtemos uma "árvore" desses tokens e podemos andar por ela, procurar, mudar, deletar e adicionar elementos.

2. Lendo JSON desconhecido

Suponha que recebemos este JSON e não queremos ou não podemos escrever uma classe para ele de antemão:

{
  "status": "ok",
  "amount": 150.5,
  "items": [
    {
      "name": "book",
      "qty": 1
    },
    {
      "name": "pen",
      "qty": 3
    }
  ]
}

Com Newtonsoft.Json podemos transformar isso em uma árvore e inspecionar "on the fly".

Exemplo: ler JSON em JObject

using Newtonsoft.Json.Linq;

string json = @"{
  ""status"": ""ok"",
  ""amount"": 150.5,
  ""items"": [
    { ""name"": ""book"", ""qty"": 1 },
    { ""name"": ""pen"", ""qty"": 3 }
  ]
}";

// Parse da string e obtemos a árvore
JObject root = JObject.Parse(json);

// Pegando propriedades como num dicionário
string status = (string)root["status"]; // "ok"
double amount = (double)root["amount"]; // 150.5

// items — é um array, logo JArray
JArray items = (JArray)root["items"];

// Iterando o array
foreach (JObject item in items)
{
    string name = (string)item["name"];
    int qty = (int)item["qty"];
    Console.WriteLine($"Produto: {name}, Qtde: {qty}");
}

Extremamente conveniente: sem declarar classes — dá pra extrair rapidamente os pedaços que precisa.

3. Indexadores e acesso dinâmico

Indexadores:

  • Para objeto: root["status"], root["items"]
  • Para array: items[0], items[1]

Para obter o valor já no tipo desejado, use cast (string), (int), (bool), (double) — a biblioteca converte automaticamente o tipo.

Se os dados podem estar faltando, cuidado: acessar uma chave ausente retornará null, e o cast explícito vai falhar. Melhor usar métodos com checagem:

if (root.TryGetValue("amount", out var token))
{
    double amount = token.Value<double>();
    // Esse jeito é mais conveniente: Value<T>() – converte direto
}

Objetos e arrays aninhados também são lidos facilmente:

// Pegar o segundo produto
JObject secondItem = (JObject)root["items"][1];
string itemName = (string)secondItem["name"]; // "pen"

Também dá pra usar dinamicamente:

dynamic droot = root;
Console.WriteLine(droot.status); // "ok"

Mas lembre-se: com dynamic o compilador não checa o acesso aos campos — os erros aparecem só em runtime.

4. Modificando a árvore JSON on the fly

Adicionando elementos

root["currency"] = "RUB";        // Adicionou nova propriedade
items.Add(new JObject
{
    ["name"] = "eraser",
    ["qty"] = 2
});

Alteração e remoção

root["status"] = "done";         // Alterou o valor
items[0]["qty"] = 5;             // Aumentou a quantidade do primeiro produto
items.RemoveAt(1);               // Removeu o segundo produto

Salvar o resultado em uma string

string modifiedJson = root.ToString();
// Ou root.ToString(Formatting.Indented) para ficar bonito

5. Criando uma estrutura JSON do zero

var person = new JObject
{
    ["name"] = "Alice",
    ["age"] = 22,
    ["languages"] = new JArray { "C#", "Python" },
    ["isStudent"] = true
};

Console.WriteLine(person.ToString(Newtonsoft.Json.Formatting.Indented));
{
  "name": "Alice",
  "age": 22,
  "languages": [
    "C#",
    "Python"
  ],
  "isStudent": true
}

Útil quando você precisa retornar para uma API externa apenas parte dos dados ou montar JSON baseado em condições.

6. Como montar seu próprio objeto a partir de JObject

Às vezes é preciso transformar um JSON flexível em um objeto C# estrito. Opções:

  • Deserialização normal: JsonConvert.DeserializeObject<MyClass>(...).
  • Montagem manual do objeto, extraindo os campos necessários do JObject.
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Suponha que recebemos este JSON:
string incoming = @"{ ""name"":""Bob"", ""age"":30, ""extraField"":true }";

JObject j = JObject.Parse(incoming);

// Montando o objeto manualmente — só os campos que precisamos
var person = new Person
{
    Name = (string)j["name"],
    Age = (int)j["age"],
    // extraField não nos interessa — e tudo bem!
};

7. Exemplos de erros e nuances: armadilhas típicas

Trabalhar com estruturas JSON dinâmicas é flexível, mas tem "pedras no caminho":

  • Acessar uma chave/elemento que não existe retorna null; o cast explícito para tipo de valor vai lançar exceção.
  • Descompasso de tipo esperado (esperava um objeto, veio um valor) — erro de cast.
  • Para iterar os campos de um objeto use Properties():
foreach (var prop in root.Properties())
{
    Console.WriteLine($"Campo: {prop.Name}, Valor: {prop.Value}");
}
  • Com Newtonsoft.Json é confortável trabalhar com queries estilo LINQ (filtragem, busca):
var expensiveItems = items.Where(obj => (int)obj["qty"] > 2);
foreach (var item in expensiveItems)
    Console.WriteLine(item);

8. Erros comuns ao trabalhar com JObject/JArray

Erro #1: ausência do campo esperado ou incompatibilidade de tipo. Muito comum o dev contar que um objeto terá certo campo, mas ele não existe ou é de outro tipo. Se espera um objeto e vem um número, o cast vai lançar exceção. Verifique existência e tipo antes de usar.

Erro #2: acessar campos de estruturas aninhadas sem checar null. Quando há objetos aninhados e as chaves diferem ou faltam, tentar acessar um campo ausente pode causar falha. Verifique null antes de ler valores de nós filhos.

Erro #3: cast para tipo de valor quando o valor é null. Se a chave existe mas o valor é null, uma expressão tipo (int)j["age"] vai lançar exceção. Use Value<T>() — ele retorna o valor padrão (para int é 0, para strings — null).

Erro #4: usar demais objetos dinâmicos em vez de modelos claros. Estruturas muito complexas são mais seguras e legíveis se descritas com classes C#: isso reduz bugs e melhora a clareza. Use dinâmica quando a estrutura realmente for desconhecida ou variar muito.

Agora você sabe como parsear, modificar e criar estruturas JSON rapidamente e com segurança usando JObject e JArray, mesmo sem modelos pré-definidos.

2
Tarefa
C# SELF, nível 47, lição 3
Bloqueado
Geração de JSON "on the fly"
Geração de JSON "on the fly"
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION