¿Cómo se agrega un JToken a un JObject?

84

Estoy tratando de agregar un objeto JSON de un texto a un archivo JSON existente usando JSON.Net. Por ejemplo, si tengo los datos JSON de la siguiente manera:

  {
  "food": {
    "fruit": {
      "apple": {
        "colour": "red",
        "size": "small"
      },
      "orange": {
        "colour": "orange",
        "size": "large"
      }
    }
  }
}

He estado intentando hacer esto así:

var foodJsonObj = JObject.Parse(jsonText);
var bananaJson = JObject.Parse(@"{ ""banana"" : { ""colour"": ""yellow"", ""size"": ""medium""}}");
var bananaToken = bananaJson as JToken;
foodJsonObj["food"]["fruit"]["orange"].AddAfterSelf(bananaToken);

Pero esto da el error: "Newtonsoft.Json.Linq.JProperty cannot have multiple values."

De hecho, probé algunas formas diferentes, pero parece que no puedo llegar a ninguna parte. En mi ejemplo, lo que realmente quiero hacer es agregar el nuevo elemento a "fruta". Por favor, avíseme si hay una mejor manera de hacer esto o una biblioteca más simple de usar.

Jim
fuente

Respuestas:

139

Creo que te estás confundiendo acerca de qué puede contener qué en JSON.Net.

  • A JTokenes una representación genérica de un valor JSON de cualquier tipo. Podría ser una cadena, objeto, matriz, propiedad, etc.
  • A JPropertyes un JTokenvalor único emparejado con un nombre. Solo se puede agregar a a JObject, y su valor no puede ser otro JProperty.
  • A JObjectes una colección de JProperties. No puede contener ningún otro tipo JTokendirectamente.

En su código, está intentando agregar un JObject(el que contiene los datos de "banana") a un JProperty("naranja") que ya tiene un valor (un que JObjectcontiene {"colour":"orange","size":"large"}). Como vio, esto resultará en un error.

Lo que realmente quieres hacer es agregar un JPropertyllamado "plátano" al JObjectque contiene la otra fruta JProperties. Aquí está el código revisado:

JObject foodJsonObj = JObject.Parse(jsonText);
JObject fruits = foodJsonObj["food"]["fruit"] as JObject;
fruits.Add("banana", JObject.Parse(@"{""colour"":""yellow"",""size"":""medium""}"));
Brian Rogers
fuente
7
Nota: Utilice 'JToken.Parse' (o 'JToken.FromObject') en lugar de JObject.Parse, en la última línea. Como esto también funciona para objetos simples como cuerdas.
ctrl-alt-delor
qué hacer si quiero sobrescribir un Jproperty.
Daniel
@Daniel - Si por "sobrescribir" quieres reemplazar completamente el JPropertypor uno diferente, entonces puedes usar el Replacemétodo. Si solo desea cambiar el valor de JProperty, puede establecer la Valuepropiedad en él; se puede escribir. Consulte la referencia de la API de LINQ-to-JSON .
Brian Rogers
35

TL; DR: Debe agregar una JProperty a un JObject. Sencillo. La consulta de índice devuelve un JValue, así que averigüe cómo obtener JProperty en su lugar :)


La respuesta aceptada no responde a la pregunta como parece. ¿Qué sucede si quiero agregar específicamente una JProperty después de una específica? Primero, comencemos con terminologías que realmente me hicieron pensar.

  • JToken = La madre de todos los demás tipos. Puede ser A JValue, JProperty, JArray o JObject. Esto es para proporcionar un diseño modular al mecanismo de análisis.
  • JValue = cualquier tipo de valor Json (cadena, int, booleano).
  • JProperty = cualquier JValue o JContainer (ver más abajo) emparejado con un nombre (identificador) . Por ejemplo "name":"value".
  • JContainer = La madre de todos los tipos que contienen otros tipos (JObject, JValue).
  • JObject = un tipo JContainer que contiene una colección de JProperties
  • JArray = un tipo JContainer que contiene una colección JValue o JContainer.

Ahora, cuando consulta el elemento Json usando el índice [], obtiene el JToken sin el identificador, que podría ser un JContainer o un JValue (requiere conversión), pero no puede agregar nada después, porque es solo un valor. Puede cambiarlo por sí mismo, consultar valores más profundos, pero no puede agregar nada después, por ejemplo.

Lo que realmente desea obtener es la propiedad en su conjunto y luego agregar otra propiedad después de ella como desee. Para esto, use JOjbect.Property("name"), y luego cree otra JProperty de su deseo y luego agréguela después de este AddAfterSelfmétodo de uso . Entonces ha terminado.

Para obtener más información: http://www.newtonsoft.com/json/help/html/ModifyJson.htm

Este es el código que modifiqué.

public class Program
{
  public static void Main()
  {
    try
    {
      string jsonText = @"
      {
        ""food"": {
          ""fruit"": {
            ""apple"": {
              ""colour"": ""red"",
              ""size"": ""small""
            },
            ""orange"": {
              ""colour"": ""orange"",
              ""size"": ""large""
            }
          }
        }
      }";

      var foodJsonObj = JObject.Parse(jsonText);
      var bananaJson = JObject.Parse(@"{ ""banana"" : { ""colour"": ""yellow"", ""size"": ""medium""}}");

      var fruitJObject = foodJsonObj["food"]["fruit"] as JObject;
      fruitJObject.Property("orange").AddAfterSelf(new JProperty("banana", fruitJObject));

      Console.WriteLine(foodJsonObj.ToString());
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
    }
  }
}
Ghasan
fuente
4
Tiene mal la terminología. JToken(no JValue) es la clase base de la que JArray, JObject, JPropertyy JValueen última instancia Heredar. JValuees una primitiva JSON que representa una cadena, número, booleano, fecha o valor nulo; no puede representar una matriz u objeto. Consulte la Referencia de la API de LINQ-to-JSON .
Brian Rogers
1
@BrianRogers Gracias, arreglé la terminología.
Ghasan
1
Esta es una muy buena explicación del modelo de objetos de Newtonsoft y ahora puedo entender mucho mejor newtonsoft.com/json/help/html/N_Newtonsoft_Json_Linq.htm . Hasta ahora, mis esfuerzos por usarlo se han visto obstaculizados por la falta de una explicación detallada en los documentos. Simplemente parece ser una referencia generada automáticamente a partir de comentarios de código.
Tom Wilson
2
Sin embargo, el mod de ejemplo de código no es del todo correcto: `var foodJsonObj = JObject.Parse (jsonText); var bananaJson = JObject.Parse (@ "{" "color" ":" "amarillo" "," "tamaño" ":" "medio" "}"); var fruitJObject = foodJsonObj ["food"] ["fruit"] como JObject; fruitJObject.Property ("naranja"). AddAfterSelf (nueva JProperty ("banana", bananaJson)); `
Tom Wilson
No puedo por mi vida aplicar marcado de código a mi comentario anterior - ¡AARRGHH! He usado marcas traseras y he sangrado 4 espacios, ¿cómo lo hago?
Tom Wilson
5

Simplemente agregando .Firsta su bananaTokendebería hacerlo: básicamente se mueve más allá del para convertirlo en un en lugar de un .
foodJsonObj["food"]["fruit"]["orange"].Parent.AddAfterSelf(bananaToken .First);
.First{JPropertyJToken

@Brian Rogers, Gracias, olvidé el .Parent. Editado

RepDbg
fuente