Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
241 views
in Technique[技术] by (71.8m points)

c# - JSON.NET Serializes Empty JSON

I am using MetadataType to define Json.NET attributes for the following type, then serializing it using Json.NET inside its ToString() method:

namespace ConsoleApp1
{
    public interface ICell
    {
        int Id { get; }
    }
    public interface IEukaryote
    {
        System.Collections.Generic.IEnumerable<ICell> Cells { get; }
        string GenericName { get; }
    }
    public sealed partial class PlantCell
        : ICell
    {
        public int Id => 12324;
    }
    public sealed partial class Plant
        : IEukaryote
    {
        private readonly System.Collections.Generic.IDictionary<string, object> _valuesDict;
        public Plant()
        {
            _valuesDict = new System.Collections.Generic.Dictionary<string, object>();
            var cells = new System.Collections.Generic.List<PlantCell>();
            cells.Add(new PlantCell());
            _valuesDict["Cells"] = cells;
            _valuesDict["GenericName"] = "HousePlant";
        }
        public System.Collections.Generic.IEnumerable<ICell> Cells => _valuesDict["Cells"] as System.Collections.Generic.IEnumerable<ICell>;
        public string GenericName => _valuesDict["GenericName"] as string;
        public int SomethingIDoNotWantSerialized => 99999;
        public override string ToString()
        {
            return Newtonsoft.Json.JsonConvert.SerializeObject(this,
                new Newtonsoft.Json.JsonSerializerSettings()
                {
                    ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
                }
            );
        }
    }
    [System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
    public sealed partial class Plant
    {
        [Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
        internal sealed class PlantMetadata
        {
            [Newtonsoft.Json.JsonProperty]
            public System.Collections.Generic.IEnumerable<ICell> Cells;
            [Newtonsoft.Json.JsonProperty]
            public string GenericName;
            //...
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var plant = new Plant();
            System.Console.WriteLine(System.String.Format("Output is {0}", plant.ToString()));
            System.Console.ReadKey();
        }
    }
}

My problem is that Plant.ToString() will return '{}'. Why is that? It was working before. The only change I made was in PlantMetadata where I altered the MemberSerialization to OptIn instead of OptOut, as I had less properties I wanted included than left out.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As stated by Newtonsoft in this issue, MetadataTypeAttribute attributes are in fact supported by Json.NET. However, it appears that Json.NET requires that the MetadataClassType members must be properties when the corresponding "real" members are properties, and fields when the corresponding "real" members are fields. Thus, if I define your Plant type as follows, with two properties and one field to be serialized:

public sealed partial class Plant : IEukaryote
{
    public System.Collections.Generic.IEnumerable<ICell> Cells { get { return (_valuesDict["Cells"] as System.Collections.IEnumerable).Cast<ICell>(); } }
    public string GenericName { get { return _valuesDict["GenericName"] as string; } }
    public string FieldIWantSerialized;
    public int SomethingIDoNotWantSerialized { get { return 99999; } }

    // Remainder as before.

Then the PlantMetadata must also have two properties and one field for them to be serialized successfully:

//Metadata.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
    [JsonObject(MemberSerialization.OptIn)]
    internal sealed class PlantMetadata
    {
        [JsonProperty]
        public IEnumerable<ICell> Cells { get; set; }

        [JsonProperty]
        public string GenericName { get; set; }

        [JsonProperty]
        public string FieldIWantSerialized;
    }
}

If I make Cells or GenericName be fields, or FieldIWantSerialized be a property, then they do not get opted into serialization.

Sample working .Net Fiddle.

Note that, in addition, I have found that the MetadataClassType properties apparently must have the same return type as the real properties. If I change your PlantMetadata as follows:

[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
    [JsonProperty]
    public object Cells { get; set; }

    [JsonProperty]
    public object GenericName { get; set; }

    [JsonProperty]
    public object FieldIWantSerialized;
} 

Then only FieldIWantSerialized is serialized, not the properties. .Net Fiddle #2 showing this behavior. This may be a Newtonsoft issue; as stated in the Microsoft documentation Defining Attributes in Metadata Classes:

The actual type of these properties is not important, and is ignored by the compiler. The accepted approach is to declare them all as of type Object.

If it matters, you could report an issue about the return type restriction to Newtonsoft - or report an issue asking that details of their support for MetadataTypeAttribute be more fully documented.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...