更新的回答:
查看JSON.Net如何映射XML,它采取的方法是看到什么就序列化什么,但如果看到多个相同节点,则会创建一个数组。这对于许多具有一致布局的XML DOM树非常有用,但不幸的是不能满足您的需求。
您可以通过查看以下文件源中
SerializeGroupedNodes()
和
SerializeNode()
函数的主体来验证此内容。
XmlNodeConverter.cs source code @ CodePlex, ChangeSet #63616
还有另一种选择,我之前认为可能过度了,但现在我们知道如何从序列化端默认行为中获得帮助,这将非常有用。
Json.Net支持使用派生自
JsonConverter
的自定义转换器,以基于情况按案例映射特定值。
我们可以在序列化端或反序列化端处理此问题。我选择在反序列化端编写解决方案,因为您可能已经有其他现有原因将XML映射到JSON。
一个很大的好处是你的类可以保持完整,只需要应用属性来覆盖。下面是一个代码示例,演示如何使用
JsonAttribute
和自定义转换器类(
MMArrayConverter
)来解决你的问题。请注意,你可能需要更加彻底地测试它,并且也许需要更新转换器以处理其他情况,比如如果你最终迁移到
IList<string>
或一些其他奇怪的情况,或者甚至使它与泛型一起工作。
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)
{
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)
{
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();
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();
}
}
}
原始回答:
最好在源头修复你收到的 JSON,正如许多人已经指出的那样。你可能希望发布一个更新,展示你如何将你更新的评论中的 XML 映射到 JSON,因为这将是最好的路线。
然而,如果你发现这不可能,并且想要一些方法来序列化和处理变量值,那么你可以声明 mm
为类型 object
,然后使用 JSON.Net 的 Linq 支持自己处理可能的情况。在你描述的两种情况中,你会发现将 mm
声明为类型 object
将导致调用 DeserializeObject<>
时将 null
、string
或 JArray
分配给 mm
。
以下是一个代码示例,展示了这一操作的实现方法。在其他情况下,您可能会收到一个
JObject
,这也在此示例中进行了说明。请注意,成员函数
mmAsList()
将为您修补差异。还请注意,我通过返回
null
来处理
null
,对于
List<string>
,您可能需要根据实现情况进行修改。
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();
}
}
}
mm
可能包含多个元素,则应始终将其表示为数组([]
),而不是简单的name: value
对。 - Kirk Woll