C# WebAPI JSON序列化不序列化复合对象的任何子对象

C# WebAPI JSON序列化不序列化复合对象的任何子对象,c#,json,serialization,asp.net-web-api,json.net,C#,Json,Serialization,Asp.net Web Api,Json.net,因此,我需要将一个组合序列化为JSON(使用JSON.NET),并希望带着这个问题来到这里将是一个快速的胜利 我有一个非常基本的复合实现,我只是尝试使用它来构建我的服务和数据结构,但是JSONSerializer只是序列化根节点 代码: 唯一被序列化的是 {"Name\":\"Root\"}" 有人知道这不是序列化子元素的原因吗? 我希望这是件愚蠢的事 编辑1 我以前从未尝试过用WebAPI将图形序列化为JSON。我是否需要编写自定义MediaTypeFormatter来序列化此文件 Edit

因此,我需要将一个组合序列化为JSON(使用JSON.NET),并希望带着这个问题来到这里将是一个快速的胜利

我有一个非常基本的复合实现,我只是尝试使用它来构建我的服务和数据结构,但是JSONSerializer只是序列化根节点

代码:

唯一被序列化的是

{"Name\":\"Root\"}"
有人知道这不是序列化子元素的原因吗? 我希望这是件愚蠢的事

编辑1 我以前从未尝试过用WebAPI将图形序列化为JSON。我是否需要编写自定义MediaTypeFormatter来序列化此文件

Edit2(添加所需输出) 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"}
                      ]
          ]
}
如果您不想使用Datacontract,我认为您必须使用一些您需要的方法来实现JsonConverter。
名称空间JsonOutil
{
公共类TestConverter:JsonConverter
{
公共覆盖布尔CanConvert(System.Type objectType)
{
返回objectType==typeof(yourClasse);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
object retVal=新对象();
if(reader.TokenType==JsonToken.StartObject)
{
反序列化(reader,typeof(T));
retVal=new List(){instance};
}
else if(reader.TokenType==JsonToken.StartArray)
{
retVal=序列化程序。反序列化(读取器,对象类型);
}
返回返回;
}
公共重写对象ReadJson(JsonReader阅读器、System.Type对象类型、对象existingValue、JsonSerializer序列化程序)
{
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
抛出新系统。NotImplementedException();
}
读取时的公共字符串GetValue(字典值、字符串键)
{
return!values.ContainsKey(key)| | values[key]==null
无效的
:value[key].ToString();
}
}
}

由于组合中的元素列表是私有的,因此未序列化子元素。默认情况下,Json.Net不会序列化私有成员。如果使用
[JsonProperty(“Elements”)]
标记列表,则子项将被序列化

public class Composite: Element
{
   ...
   [JsonProperty("Elements")]
   private List<Element> Elements { get; set; }
   ...
}
编辑

好的,下面是复合材料的一个转换器示例:

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();
    }
}
我意识到这与您要求的JSON不完全相同,但它应该让您朝着正确的方向前进。您在问题中指定的“所需”JSON的一个问题是它不是完全有效的。命名属性只能位于对象内部,不能直接位于数组内部。在第二个示例中,在“Branch2”的数组中直接有一个名为“Branch3”的属性。这行不通。因此,您需要将Branch2改为对象。但如果您这样做,那么您的组合就有了一个不一致的表示:如果它只包含叶子,那么它就是一个数组,否则它就是一个对象。可以使用转换器根据内容更改组合的表示形式(事实上,我成功创建了这样一个beast),但这会使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();
    }
}

我看到的一个问题是元素列表是私有的。默认情况下,Json.Net不会序列化私有成员。如果您用
[JsonProperty(“Elements”)]
标记它,它将开始序列化它。但是,预期的JSON输出是什么?您可能需要一个转换器,具体取决于您实际希望JSON的外观。您不需要自定义MediaTypeFormatter来序列化对象图。@BrianRogers Re:“预期JSON输出”我需要指定给分支的名称作为根的子元素的属性名。终端节点是复杂的数据对象。转换器?@BrianRogers和顺便说一句…元素上的私人修饰符是罪魁祸首。我在浏览代码的过程中忽略了这一点,并立即将代码扔到这里以供审阅。是的,要根据组合的名称更改元素列表的属性名称,您需要一个转换器。你需要这方面的帮助吗,或者你对这一部分有把握吗?这根本不能回答问题——他是在尝试序列化,而不是反序列化。“你在问题中指定的“期望的”JSON的一个问题是它不完全有效。”你说得很对,我很高兴我在手写时犯了这个错误。这是我想要的JSON输出(复合数据和所有内容),但您的观察非常有用。我在angular控制器中直接创建了模拟json数据,我正在通过此任务将其下推到服务层。目前,我可以合理地假设树只有3个复合级别,第4个级别包含叶节点:(续)根->合成->收集->项是此数据获得的最复杂的,至少目前是这样。我选择了一棵树,因为嵌套级别根据构图的面积而变化。一个分支在第三级终止,另一个分支在第二级终止,还有几个分支在第四级终止,但我没有一套DataContract来创建具体的模板。我认为合成材料是最好的选择
public class Composite: Element
{
   ...
   [JsonProperty("Elements")]
   private List<Element> Elements { get; set; }
   ...
}
{
  "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);
    }
}
{
  "Root": [
    {
      "Branch1": [
        {
          "Foo": "Bar",
          "Name": "Leaf1"
        },
        {
          "Foo": "Baz",
          "Name": "Leaf2"
        }
      ]
    },
    {
      "Branch2": [
        {
          "Foo": "Quux",
          "Name": "Leaf3"
        },
        {
          "Branch3": [
            {
              "Foo": "Fizz",
              "Name": "Leaf4"
            }
          ]
        }
      ]
    }
  ]
}
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"
      }
    ]
  }
}