所以我需要将一个复合体序列化为JSON(使用JSON.NET),并且希望能够快速获得这个问题。
我有一个非常基本的复合实现,我只是试图用来支持我的服务和数据结构,但JSONSerializer只是序列化根节点。
代码:
namespace Data
{
public abstract class Element
{
protected string _name;
public Element(string name)
{
_name = name;
}
public abstract void Add(Element element);
public string Name { get { return _name; } }
}
public class ConcreteElement : Element
{
public ConcreteElement(string name) : base(name) { }
public override void Add(Element element)
{
throw new InvalidOperationException("ConcreteElements may not contain Child nodes. Perhaps you intended to add this to a Composite");
}
}
public class Composite: Element
{
public Composite(string name) : base(name) { Elements = new List<Element>(); }
private List<Element> Elements { get; set; }
public override void Add(Element element)
{
Elements.Add(element);
}
}
}
在我的Controller的HttpGet方法中,
Composite root = new Composite("Root");
Composite branch = new Composite("Branch");
branch.Add(new ConcreteElement("Leaf1"));
branch.Add(new ConcreteElement("Leaf2"));
root.Add(branch);
return JsonConvert.SerializeObject(root);
唯一被序列化的是
{"Name\":\"Root\"}"
任何人都可以看到这不是序列化子元素的原因吗? 我希望它有点蠢。
我之前从未尝试过使用WebAPI将图表序列化为JSON。我是否需要编写自定义MediaTypeFormatter来序列化它?
Leaf1和Leaf2目前只是标记。一旦我可以将它序列化,它们本身就是复杂的对象。 所以,目前......
{
"Name" : "Root"
,"Branch":
[
{"Name":"Leaf1"}
,{"Name":"Leaf2"}
]
]
}
并最终
{
"Name" : "Root"
,"Branch1":
[
{"Name":"Leaf1", "Foo":"Bar"}
{"Name":"Leaf2", "Foo":"Baz"}
]
,"Branch2":
[
"Branch3":[
{"Name":"Leaf3", "Foo":"Quux"}
]
]
}
答案 0 :(得分:5)
子元素未被序列化,因为Composite中的元素列表是私有的。默认情况下,Json.Net不会序列化私有成员。如果您使用[JsonProperty("Elements")]
标记列表,则会对子项进行序列化。
public class Composite: Element
{
...
[JsonProperty("Elements")]
private List<Element> Elements { get; set; }
...
}
如果您使用此更改运行示例代码,则应获得以下JSON:
{
"Elements": [
{
"Elements": [
{
"Name": "Leaf1"
},
{
"Name": "Leaf2"
}
],
"Name": "Branch"
}
],
"Name": "Root"
}
修改强>
好的,这是您的复合材料的示例转换器:
class CompositeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Composite));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Composite composite = (Composite)value;
// Need to use reflection here because Elements is private
PropertyInfo prop = typeof(Composite).GetProperty("Elements", BindingFlags.NonPublic | BindingFlags.Instance);
List<Element> children = (List<Element>)prop.GetValue(composite);
JArray array = new JArray();
foreach (Element e in children)
{
array.Add(JToken.FromObject(e, serializer));
}
JObject obj = new JObject();
obj.Add(composite.Name, array);
obj.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
这是一个演示:
class Program
{
static void Main(string[] args)
{
Composite root = new Composite("Root");
Composite branch1 = new Composite("Branch1");
branch1.Add(new ConcreteElement("Leaf1", "Bar"));
branch1.Add(new ConcreteElement("Leaf2", "Baz"));
root.Add(branch1);
Composite branch2 = new Composite("Branch2");
branch2.Add(new ConcreteElement("Leaf3", "Quux"));
Composite branch3 = new Composite("Branch3");
branch3.Add(new ConcreteElement("Leaf4", "Fizz"));
branch2.Add(branch3);
root.Add(branch2);
string json = JsonConvert.SerializeObject(root, Formatting.Indented, new CompositeConverter());
Console.WriteLine(json);
}
}
public abstract class Element
{
protected string _name;
public Element(string name)
{
_name = name;
}
public abstract void Add(Element element);
public string Name { get { return _name; } }
}
public class ConcreteElement : Element
{
public ConcreteElement(string name, string foo) : base(name)
{
Foo = foo;
}
public string Foo { get; set; }
public override void Add(Element element)
{
throw new InvalidOperationException("ConcreteElements may not contain Child nodes. Perhaps you intended to add this to a Composite");
}
}
public class Composite : Element
{
public Composite(string name) : base(name) { Elements = new List<Element>(); }
private List<Element> Elements { get; set; }
public override void Add(Element element)
{
Elements.Add(element);
}
}
以下是生成的JSON输出:
{
"Root": [
{
"Branch1": [
{
"Foo": "Bar",
"Name": "Leaf1"
},
{
"Foo": "Baz",
"Name": "Leaf2"
}
]
},
{
"Branch2": [
{
"Foo": "Quux",
"Name": "Leaf3"
},
{
"Branch3": [
{
"Foo": "Fizz",
"Name": "Leaf4"
}
]
}
]
}
]
}
我意识到这与您要求的JSON不完全相同,但它应该让您朝着正确的方向前进。您在问题中指定的“所需”JSON的一个问题是它不完全有效。命名属性只能在对象内,而不能直接在数组内。在第二个示例中,您在数组中直接为“Branch2”命名了一个名为“Branch3”的属性。这不行。因此,您需要将Branch2设为对象。但是如果你这样做,那么你的复合表示不一致:如果它只包含叶子,那么它就是一个数组,否则就是一个对象。有可能使转换器根据内容更改组合的表示(事实上我设法创建了这样的野兽),但这使得JSON更难以消耗,最后我不认为你我会想用它。如果你很好奇,我在下面列出了这个备用转换器及其输出。
class CompositeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Composite));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
Composite composite = (Composite)value;
// Need to use reflection here because Elements is private
PropertyInfo prop = typeof(Composite).GetProperty("Elements", BindingFlags.NonPublic | BindingFlags.Instance);
List<Element> children = (List<Element>)prop.GetValue(composite);
// if all children are leaves, output as an array
if (children.All(el => el.GetType() != typeof(Composite)))
{
JArray array = new JArray();
foreach (Element e in children)
{
array.Add(JToken.FromObject(e, serializer));
}
array.WriteTo(writer);
}
else
{
// otherwise use an object
JObject obj = new JObject();
if (composite.Name == "Root")
{
obj.Add("Name", composite.Name);
}
foreach (Element e in children)
{
obj.Add(e.Name, JToken.FromObject(e, serializer));
}
obj.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
使用相同数据输出:
{
"Name": "Root",
"Branch1": [
{
"Foo": "Bar",
"Name": "Leaf1"
},
{
"Foo": "Baz",
"Name": "Leaf2"
}
],
"Branch2": {
"Leaf3": {
"Foo": "Quux",
"Name": "Leaf3"
},
"Branch3": [
{
"Foo": "Fizz",
"Name": "Leaf4"
}
]
}
}
答案 1 :(得分:-1)
If you don't want to use Datacontract , i think you have to implement JsonConverter with some the methodes that you need.
namespace JsonOutil
{
public class TestConverter<T> : JsonConverter
{
public override bool CanConvert(System.Type objectType)
{
return objectType == typeof(yourClasse);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object retVal = new Object();
if (reader.TokenType == JsonToken.StartObject)
{
T instance = (T)serializer.Deserialize(reader, typeof(T));
retVal = new List<T>() { instance };
}
else if (reader.TokenType == JsonToken.StartArray)
{
retVal = serializer.Deserialize(reader, objectType);
}
return retVal;
}
public override object ReadJson(JsonReader reader, System.Type objectType, object existingValue, JsonSerializer serializer)
{
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new System.NotImplementedException();
}
public string GetValueWhenReading(Dictionary<string, object> values, string key)
{
return !values.ContainsKey(key) || values[key] == null
? null
: values[key].ToString();
}
}
}