respuesta Actualizado:
En cuanto a cómo los mapas Json.NET XML, se adopta el enfoque de que lo que ve es lo que serializa, excepto que si ve múltiplos, hará una matriz. Esto es ideal para muchos árboles XML DOM con un diseño consistente, pero desafortunadamente no puede funcionar para sus propósitos.
Puede verificar esto mirando el cuerpo para las funciones SerializeGroupedNodes()
y SerializeNode()
en la siguiente fuente de archivo.
XmlNodeConverter.cs source code @ CodePlex, ChangeSet #63616
Hay otra opción que me previamente pensó que podría ser una exageración, pero sería útil ahora que sabemos qué esperar del comportamiento por defecto en el extremo serialización.
Json.Net admite el uso de convertidores personalizados derivados de JsonConverter
para mapear casos particulares de valores caso por caso.
Podemos manejar esto ya sea en el lado de serialización o en el lado de deserialización. He elegido escribir una solución en el lado deserializador, ya que probablemente tenga otras razones existentes para asignar XML a JSON.
Una gran ventaja es que su clase puede permanecer intacta, excepto la anulación, que requiere la aplicación de un atributo. Aquí hay un ejemplo de código que demuestra cómo usar JsonAttribute
y una clase de convertidor personalizada (MMArrayConverter
) para solucionar su problema. Tenga en cuenta que probablemente quiera probar esto más a fondo y quizás actualice el convertidor para manejar otros casos, por ejemplo, si finalmente migra al IList<string>
o algún otro caso funky como Lazy<List<string>>
, o incluso hacerlo funcionar con genéricos.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
namespace JsonArrayImplictConvertTest
{
public class MMArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.Equals(typeof(List<string>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
List<string> parseList = new List<string>();
do
{
if (reader.Read())
{
if (reader.TokenType == JsonToken.String)
{
parseList.Add((string)reader.Value);
}
else
{
if (reader.TokenType == JsonToken.Null)
{
parseList.Add(null);
}
else
{
if (reader.TokenType != JsonToken.EndArray)
{
throw new ArgumentException(string.Format("Expected String/Null, Found JSON Token Type {0} instead", reader.TokenType.ToString()));
}
}
}
}
else
{
throw new InvalidOperationException("Broken JSON Input Detected");
}
}
while (reader.TokenType != JsonToken.EndArray);
return parseList;
}
if (reader.TokenType == JsonToken.Null)
{
// TODO: You need to decide here if we want to return an empty list, or null.
return null;
}
if (reader.TokenType == JsonToken.String)
{
List<string> singleList = new List<string>();
singleList.Add((string)reader.Value);
return singleList;
}
throw new InvalidOperationException("Unhandled case for MMArrayConverter. Check to see if this converter has been applied to the wrong serialization type.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// Not implemented for brevity, but you could add this if needed.
throw new NotImplementedException();
}
}
public class ModifiedXX
{
public string yy { get; set; }
[JsonConverter(typeof(MMArrayConverter))]
public List<string> mm { get; set; }
public void Display()
{
Console.WriteLine("yy is {0}", this.yy);
if (null == mm)
{
Console.WriteLine("mm is null");
}
else
{
Console.WriteLine("mm contains these items:");
mm.ForEach((item) => { Console.WriteLine(" {0}", item); });
}
}
}
class Program
{
static void Main(string[] args)
{
string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }";
ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1);
obj1.Display();
string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }";
ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2);
obj2.Display();
// This test is now required in case we messed up the parser state in our converter.
string jsonTest3 = "[{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] },{\"yy\":\"nn\", \"mm\": \"zzz\" }]";
List<ModifiedXX> obj3 = JsonConvert.DeserializeObject<List<ModifiedXX>>(jsonTest3);
obj3.ForEach((obj) => { obj.Display(); });
Console.ReadKey();
}
}
}
respuesta original:
que sería mejor para solucionar el JSON está recibiendo en la fuente, como muchos ya han señalado. Es posible que desee publicar una actualización que muestre cómo se correlaciona el XML en su comentario actualizado con JSON, ya que esa sería la mejor ruta en general.
Sin embargo, si encuentra que esto no es posible y quiere forma de serializar y manejar el valor de la variante después de los hechos, puede corregir las cosas declarando mm
como tipo object
, y luego manejando la posible utiliza usted mismo el soporte Linq de JSON.Net. En los dos escenarios que describió, encontrará que declarar mm
como tipo object
dará como resultado un null
, un string
, o un JArray
asignado a mm
por la llamada al DeserializeObject<>
.
Aquí hay un ejemplo de código que muestra esto en acción. También hay un caso en otras circunstancias donde podría recibir un JObject
, que también se trata en esta muestra. Tenga en cuenta que la función miembro mmAsList()
hace el trabajo de corregir la diferencia por usted. También tenga en cuenta que he manejado null
aquí devolviendo un null
para List<string>
; es probable que desee revisar esto para su implementación.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonArrayUnionTest
{
public class ModifiedXX
{
public string yy { get; set; }
public object mm { get; set; }
public List<string> mmAsList()
{
if (null == mm) { return null; }
if (mm is JArray)
{
JArray mmArray = (JArray)mm;
return mmArray.Values<string>().ToList();
}
if (mm is JObject)
{
JObject mmObj = (JObject)mm;
if (mmObj.Type == JTokenType.String)
{
return MakeList(mmObj.Value<string>());
}
}
if (mm is string)
{
return MakeList((string)mm);
}
throw new ArgumentOutOfRangeException("unhandled case for serialized value for mm (cannot be converted to List<string>)");
}
protected List<string> MakeList(string src)
{
List<string> newList = new List<string>();
newList.Add(src);
return newList;
}
public void Display()
{
Console.WriteLine("yy is {0}", this.yy);
List<string> mmItems = mmAsList();
if (null == mmItems)
{
Console.WriteLine("mm is null");
}
else
{
Console.WriteLine("mm contains these items:");
mmItems.ForEach((item) => { Console.WriteLine(" {0}", item); });
}
}
}
class Program
{
static void Main(string[] args)
{
string jsonTest1 = "{\"yy\":\"nn\", \"mm\": [ \"zzz\", \"aaa\" ] }";
ModifiedXX obj1 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest1);
obj1.Display();
string jsonTest2 = "{\"yy\":\"nn\", \"mm\": \"zzz\" }";
ModifiedXX obj2 = JsonConvert.DeserializeObject<ModifiedXX>(jsonTest2);
obj2.Display();
Console.ReadKey();
}
}
}
¿No puedes cambiar la forma en que se produjo el JSON? Porque de esta manera es bastante inconsistente y siempre producir una matriz tiene más sentido. – svick
¿Por qué el formato del objeto JSON entrante es insistente? Si 'mm' puede contener más de un elemento, siempre se debe transmitir como una matriz (' [] ') y nunca como un simple par' nombre: valor'. –
Debería poder realizar un masaje en el JSON antes de enviarlo al servidor para que siempre tenga el formato que funcione. Muestra el JS que usas para construir el objeto JSON. – arb