Kentico:从 Json 反序列化对象 (TreeNode)

Kentico: Deserialize object (TreeNode) from Json

我的问题很简单。 我正在处理一个 API,我需要更新一个实体。为此,我需要将来自 body 的 JObject 转换为 Kentico 模型的 TreeNode (News) 对象。

阅读 Kentico 文档有一个名为 JSonSerializer 的对象,它似乎是正确的:

Kentico 9 JsonSerializer

我的 API 看起来像这样:

[HttpPut]
[Route("")]
public IHttpActionResult UpdateById([FromBody]JObject updated)
{
    CMS.DataCom.JsonSerializer serializer = new CMS.DataCom.JsonSerializer();
    var result = serializer.Unserialize<CMS.DocumentEngine.Types.News>(updated.ToString());

    DocumentHelper.UpdateDocument(result);

    return Ok("record updated.");
}

我的 "updated" 对象具有正确的 JSON。但是,当我尝试反序列化时,出现以下错误:

An exception of type 'System.Runtime.Serialization.InvalidDataContractException' occurred in System.Runtime.Serialization.dll but was not handled in user code Additional information: Type 'CMS.DocumentEngine.Types.News' cannot be serialized. Consider marking it with the DataContractAttribute attribute, and marking all of its members you want serialized with the DataMemberAttribute attribute. If the type is a collection, consider marking it with the CollectionDataContractAttribute. See the Microsoft .NET Framework documentation for other supported types.

我自动生成的CMS.DocumentEngine.Types.Newsclass是这样定义的:

//--------------------------------------------------------------------------------------------------
// <auto-generated>
//
//     This code was generated by code generator tool.
//
//     To customize the code use your own partial class. For more info about how to use and customize
//     the generated code see the documentation at http://docs.kentico.com.
//
// </auto-generated>
//--------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;

using CMS;
using CMS.Helpers;
using CMS.DataEngine;
using CMS.DocumentEngine.Types;
using CMS.DocumentEngine;
using System.Runtime.Serialization;

[assembly: RegisterDocumentType(News.CLASS_NAME, typeof(News))]
namespace CMS.DocumentEngine.Types
{
    /// <summary>
    /// Represents a content item of type News.
    /// </summary>
    public partial class News : TreeNode
    {
        #region "Constants and variables"

        /// <summary>
        /// The name of the data class.
        /// </summary>
        public const string CLASS_NAME = "AonAffinity.News";


        /// <summary>
        /// The instance of the class that provides extended API for working with News fields.
        /// </summary>
        private readonly NewsFields mFields;

        #endregion


        #region "Properties"

        /// <summary>
        /// 
        /// </summary>
        [DatabaseIDField]
        public int NewsID
        {
            get
            {
                return ValidationHelper.GetInteger(GetValue("NewsID"), 0);
            }
            set
            {
                SetValue("NewsID", value);
            }
        }


        /// <summary>
        /// Hero Image (1650x660).
        /// </summary>
        [DatabaseField]
        public string HeroImage
        {
            get
            {
                return ValidationHelper.GetString(GetValue("HeroImage"), "");
            }
            set
            {
                SetValue("HeroImage", value);
            }
        }


        /// <summary>
        /// Heading Text.
        /// </summary>
        [DatabaseField]
        public string HeadingText
        {
            get
            {
                return ValidationHelper.GetString(GetValue("HeadingText"), "");
            }
            set
            {
                SetValue("HeadingText", value);
            }
        }


        /// <summary>
        /// Main Content.
        /// </summary>
        [DatabaseField]
        public string MainContent
        {
            get
            {
                return ValidationHelper.GetString(GetValue("MainContent"), "");
            }
            set
            {
                SetValue("MainContent", value);
            }
        }


        /// <summary>
        /// News Title.
        /// </summary>
        [DatabaseField]
        public string NewsTitle
        {
            get
            {
                return ValidationHelper.GetString(GetValue("NewsTitle"), "");
            }
            set
            {
                SetValue("NewsTitle", value);
            }
        }


        /// <summary>
        /// Release Date.
        /// </summary>
        [DatabaseField]
        public DateTime NewsReleaseDate
        {
            get
            {
                return ValidationHelper.GetDateTime(GetValue("NewsReleaseDate"), DateTimeHelper.ZERO_TIME);
            }
            set
            {
                SetValue("NewsReleaseDate", value);
            }
        }


        /// <summary>
        /// News Summary.
        /// </summary>
        [DatabaseField]
        public string NewsSummary
        {
            get
            {
                return ValidationHelper.GetString(GetValue("NewsSummary"), "");
            }
            set
            {
                SetValue("NewsSummary", value);
            }
        }


        /// <summary>
        /// News Text.
        /// </summary>
        [DatabaseField]
        public string NewsText
        {
            get
            {
                return ValidationHelper.GetString(GetValue("NewsText"), "");
            }
            set
            {
                SetValue("NewsText", value);
            }
        }


        /// <summary>
        /// Teaser Image (370x230).
        /// </summary>
        [DatabaseField]
        public string NewsTeaser
        {
            get
            {
                return ValidationHelper.GetString(GetValue("NewsTeaser"), "");
            }
            set
            {
                SetValue("NewsTeaser", value);
            }
        }


        /// <summary>
        /// This text will show up on the search results page.  It has a max length of .
        /// </summary>
        [DatabaseField]
        public string SearchDescriptionText
        {
            get
            {
                return ValidationHelper.GetString(GetValue("SearchDescriptionText"), "");
            }
            set
            {
                SetValue("SearchDescriptionText", value);
            }
        }


        /// <summary>
        /// Page Container Css Class Override.
        /// </summary>
        [DatabaseField]
        public string PageContainerCssClassOverride
        {
            get
            {
                return ValidationHelper.GetString(GetValue("PageContainerCssClassOverride"), "");
            }
            set
            {
                SetValue("PageContainerCssClassOverride", value);
            }
        }


        /// <summary>
        /// Gets an object that provides extended API for working with News fields.
        /// </summary>
        public NewsFields Fields
        {
            get
            {
                return mFields;
            }
        }


        /// <summary>
        /// Provides extended API for working with News fields.
        /// </summary>
        public partial class NewsFields
        {
            /// <summary>
            /// The content item of type News that is a target of the extended API.
            /// </summary>
            private readonly News mInstance;


            /// <summary>
            /// Initializes a new instance of the <see cref="NewsFields" /> class with the specified content item of type News.
            /// </summary>
            /// <param name="instance">The content item of type News that is a target of the extended API.</param>
            public NewsFields(News instance)
            {
                mInstance = instance;
            }


            /// <summary>
            /// 
            /// </summary>
            public int ID
            {
                get
                {
                    return mInstance.NewsID;
                }
                set
                {
                    mInstance.NewsID = value;
                }
            }


            /// <summary>
            /// Hero Image (1650x660).
            /// </summary>
            public string HeroImage
            {
                get
                {
                    return mInstance.HeroImage;
                }
                set
                {
                    mInstance.HeroImage = value;
                }
            }


            /// <summary>
            /// Heading Text.
            /// </summary>
            public string HeadingText
            {
                get
                {
                    return mInstance.HeadingText;
                }
                set
                {
                    mInstance.HeadingText = value;
                }
            }


            /// <summary>
            /// Main Content.
            /// </summary>
            public string MainContent
            {
                get
                {
                    return mInstance.MainContent;
                }
                set
                {
                    mInstance.MainContent = value;
                }
            }


            /// <summary>
            /// News Title.
            /// </summary>
            public string Title
            {
                get
                {
                    return mInstance.NewsTitle;
                }
                set
                {
                    mInstance.NewsTitle = value;
                }
            }


            /// <summary>
            /// Release Date.
            /// </summary>
            public DateTime ReleaseDate
            {
                get
                {
                    return mInstance.NewsReleaseDate;
                }
                set
                {
                    mInstance.NewsReleaseDate = value;
                }
            }


            /// <summary>
            /// News Summary.
            /// </summary>
            public string Summary
            {
                get
                {
                    return mInstance.NewsSummary;
                }
                set
                {
                    mInstance.NewsSummary = value;
                }
            }


            /// <summary>
            /// News Text.
            /// </summary>
            public string Text
            {
                get
                {
                    return mInstance.NewsText;
                }
                set
                {
                    mInstance.NewsText = value;
                }
            }


            /// <summary>
            /// Teaser Image (370x230).
            /// </summary>
            public string Teaser
            {
                get
                {
                    return mInstance.NewsTeaser;
                }
                set
                {
                    mInstance.NewsTeaser = value;
                }
            }


            /// <summary>
            /// This text will show up on the search results page.  It has a max length of .
            /// </summary>
            public string SearchDescriptionText
            {
                get
                {
                    return mInstance.SearchDescriptionText;
                }
                set
                {
                    mInstance.SearchDescriptionText = value;
                }
            }


            /// <summary>
            /// Page Container Css Class Override.
            /// </summary>
            public string PageContainerCssClassOverride
            {
                get
                {
                    return mInstance.PageContainerCssClassOverride;
                }
                set
                {
                    mInstance.PageContainerCssClassOverride = value;
                }
            }
        }

        #endregion


        #region "Constructors"

        /// <summary>
        /// Initializes a new instance of the <see cref="News" /> class.
        /// </summary>
        public News() : base(CLASS_NAME)
        {
            mFields = new NewsFields(this);
        }   

        #endregion
    }
}

同样重要的是,我已经尝试使用 System.Web.Script.Serialization.JavaScriptSerializerNewtonsoft.Json.Linq.JToken.ToObject 进行反序列化,并且直接将 class 放在 API 的参数上,但也没有用。

有什么想法吗?

我建议创建 DTO 对象,该对象将包含与您发送到控制器的 json 对象相同的属性,例如如果 json 对象是

{name: 'Roman', age: 29}

比 DTO class 会

public class PersonDTO
{
  public string Name {get; set;}
  public int Age {get; set; }
}

有了这个,你的 api 方法将如下所示:

[HttpPut]
[Route("")]
public IHttpActionResult UpdateById([FromBody]PersonDTO updated)
{...}

现在,您可以在方法内将其映射到您需要的任何内容。

通过这种方法,Web API 会为您处理所有 serialization/deserialization。

Class News继承自TreeNode class,其中有些字段(这些字段比较复杂)无法序列化。正如 Roman 所说,最好为您的对象创建一个只包含必要字段的模型。