【问题标题】:WebAPI JSON Serialization not serializing any children of a composite objectWebAPI JSON序列化不序列化复合对象的任何子对象
【发布时间】:2014-10-05 17:25:38
【问题描述】:

所以我需要将复合序列化为 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\"}"

谁能看出这不是序列化子元素的原因? 我希望这是愚蠢的。

编辑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"}
                      ]
          ]
}

【问题讨论】:

  • 我看到的一个问题是您的元素列表是私有的。 Json.Net 默认不会序列化私有成员。如果你用[JsonProperty("Elements")] 标记它,它将开始序列化它。但是,预期的 JSON 输出是什么?您可能需要一个转换器,具体取决于您实际希望 JSON 的样子。
  • 您不需要自定义 MediaTypeFormatter 来序列化对象图。
  • @BrianRogers 回复:“预期的 JSON 输出”我需要给分支的名称作为根的子元素的属性名称。和终端节点是复杂的数据对象。转换器?
  • @BrianRogers 和 btw...Elements 上的私有修饰符是罪魁祸首。我在通过代码时忽略了它,并立即将代码扔到这里以供审查。
  • 是的,要使元素列表的属性名称根据组合名称更改,您需要一个转换器。您需要这方面的帮助吗?或者您对此有帮助吗?

标签: c# json serialization asp.net-web-api json.net


【解决方案1】:

没有对子元素进行序列化,因为您的 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"
      }
    ]
  }
}

【讨论】:

  • “您在问题中指定的“所需”JSON 的一个问题是它并不完全有效。”您在这方面非常正确,我很高兴我在手写时犯了这个错误。这 我想要的 JSON 输出(作为复合数据和所有),但您的观察非常有帮助。我有我直接在我的角度控制器中制作的模拟 json 数据,我正在通过此任务将其推入服务层。目前,我可以合理地假设树的复合层只有 3 个,第 4 层包含叶节点:(续)
  • root->composition->collection->items 是这个数据得到的最复杂的,至少目前是这样。我要选择一棵树,因为嵌套级别因构图区域而异。一个分支在第三级终止,另一个在 2 终止,还有几个在 4 终止,但我没有设置 DataContract 可以让我创建具体模板。我认为复合是管理这种复杂性的最佳起点
  • 如果有帮助,我添加了我想出的另一个转换器。这个输出更接近您想要的 JSON,但同样,分支的表示是不一致的:它是一个数组还是一个对象,具体取决于子节点是否都是叶子。从好的方面来说,JSON 更紧凑。
【解决方案2】:
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();
            }
        }
    }

【讨论】:

  • 这根本没有回答问题——他正在尝试序列化,而不是反序列化。
猜你喜欢
  • 2015-11-09
  • 2015-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多