C# 如何使用Json.Net反序列化只读列表

C# 如何使用Json.Net反序列化只读列表,c#,serialization,json.net,deserialization,C#,Serialization,Json.net,Deserialization,我有一个带有内部列表的类。我不希望该类的用户能够直接与列表交互,因为我希望在返回列表之前既保持列表的排序,又运行计算(取决于顺序) 我揭露 AddItem(Item x) 和 IEnumerable<Item> Items { get { // returns a projection of internal list } } IEnumerable项 { get{//返回内部列表的投影} } 序列化工作正常,但反序列化使列表为空。我想那是因为我没有二传手。因此,我

我有一个带有内部列表的类。我不希望该类的用户能够直接与列表交互,因为我希望在返回列表之前既保持列表的排序,又运行计算(取决于顺序)

我揭露

AddItem(Item x) 

IEnumerable<Item> Items
{
     get { // returns a projection of internal list }
}
IEnumerable项
{
get{//返回内部列表的投影}
}
序列化工作正常,但反序列化使列表为空。我想那是因为我没有二传手。因此,我添加了一个允许您设置列表的选项,但前提是内部列表为空。但这并没有解决问题,结果是NewtonSoft没有调用setter,它只调用getter来获取列表,然后将每个项添加到列表中,因为我的getter返回一个投影列表,这些项被添加到一个对象中,一旦反序列化完成,该对象将立即被处理


我如何保持对列表的只读访问,同时允许一些简单的反序列化?

对我有效的方法如下:

[JsonProperty(PropertyName = "TargetName")]
private List<SomeClass> _SomeClassList { get; set; }
public IReadOnlyList<SomeClass> SomeClassList
{
    get
    {
        return this._SomeClassList.AsReadOnly();
    }
}

请参阅(谢谢佩斯卡)

我在评论部分无意中找到了关于Stackoverflow的答案,但没有投票。我在这里给你们更详细的回答:

public class State
{
        [Newtonsoft.Json.JsonProperty]
        public double Citizens { get; private set; }

        [Newtonsoft.Json.JsonProperty]
        public float Value { get { return pValue; } }
        private float pValue = 450000.0f;

        public List<string> BeachList { get; } = new List<string>();

    public State()
    {
    }

    public State(double _Citizens)
    {
        this.Citizens = _Citizens;
    }
}

...

            State croatia = new State(30.0D);
            croatia.BeachList.Add("Bol na Braču");
            croatia.BeachList.Add("Zrće");

           string croatiaSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(croatia);

           State slovenia = Newtonsoft.Json.JsonConvert.DeserializeObject<State>(croatiaSerialized);
公共类状态
{
[Newtonsoft.Json.JsonProperty]
公共双公民{get;私有集;}
[Newtonsoft.Json.JsonProperty]
公共浮点值{get{return pValue;}}
私人浮动pValue=450000.0f;
公共列表BeachList{get;}=new List();
公共国家()
{
}
公共国家(双重公民)
{
这个。公民=_公民;
}
}
...
克罗地亚州=新州(30.0D);
克罗地亚。海滩列表。添加(“Bol na Braču”);
克罗地亚。海滩名单。添加(“Zrće”);
字符串croatiserialized=Newtonsoft.Json.JsonConvert.serializedObject(克罗地亚);
State slovenia=Newtonsoft.Json.JsonConvert.DeserializeObject(克罗地亚序列化);
因此,克罗地亚和斯洛文尼亚现在都拥有相同的财产价值。 我添加了公民和价值属性,以查看您是否希望使用其中一种方式


感谢Saeb Amini()

Newtonsoft
您可以使用
CustomCreationConverter
或抽象JsonConverter,您必须实现
Create
方法和
ReadJson

ReadJson
方法是转换器调用基本方法进行默认反序列化的地方,从那里,只读集合中的每个项都可以反序列化并使用
AddItem
方法添加

任何自定义逻辑都可以在AddItem中实现

最后一步是使用属性
[JsonConverter(typeof(NavigationTreeJsonConverter))]
或在
JsonSerializerSettings

public class ItemsHolderJsonConverter : CustomCreationConverter<ItemsHolder>
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(ItemsHolder).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader,
                                   Type objectType,
                                   object existingValue,
                                   JsonSerializer serializer)
    {

        JObject jObject = JObject.Load(reader);

        ItemsHolder holder = base.ReadJson(CreateReaderFromToken(reader,jObject), objectType, existingValue, serializer) as ItemsHolder;

        var jItems = jObject[nameof(ItemsHolder.Items)] as JArray ?? new JArray();
        foreach (var jItem in jItems)
        {
            var childReader = CreateReaderFromToken(reader, jItem);
            var item = serializer.Deserialize<Item>(childReader);
            holder.AddItem(item);
        }

        return holder;
    }

    public override ItemsHolder Create(Type objectType)
    {
        return new ItemsHolder();
    }

    public static JsonReader CreateReaderFromToken(JsonReader reader, JToken token)
    {
        JsonReader jObjectReader = token.CreateReader();
        jObjectReader.Culture = reader.Culture;
        jObjectReader.DateFormatString = reader.DateFormatString;
        jObjectReader.DateParseHandling = reader.DateParseHandling;
        jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
        jObjectReader.FloatParseHandling = reader.FloatParseHandling;
        jObjectReader.MaxDepth = reader.MaxDepth;
        jObjectReader.SupportMultipleContent = reader.SupportMultipleContent;
        return jObjectReader;
    }
}
公共类ItemsHolderJsonConverter:CustomCreationConverter
{
公共覆盖布尔CanConvert(类型objectType)
{
返回类型为(ItemsHolder).IsAssignableFrom(objectType);
}
公共重写对象ReadJson(JsonReader,
类型objectType,
对象现有值,
JsonSerializer(序列化程序)
{
JObject=JObject.Load(读卡器);
ItemsHolder=base.ReadJson(CreateReaderFromToken(reader,jObject),objectType,existingValue,serializer)作为ItemsHolder;
var jItems=jObject[nameof(ItemsHolder.Items)]作为JArray??新JArray();
foreach(jItems中的var jItem)
{
var childReader=CreateReaderFromToken(读取器,jItem);
var item=serializer.Deserialize(childReader);
持有人.附件(项目);
}
报税表持有人;
}
公共重写项文件夹创建(类型objectType)
{
返回新的ItemsHolder();
}
公共静态JsonReader CreateReaderFromToken(JsonReader阅读器,JToken令牌)
{
JsonReader jObjectReader=token.CreateReader();
jObjectReader.Culture=reader.Culture;
jObjectReader.DateFormatString=reader.DateFormatString;
jObjectReader.DateParseHandling=reader.DateParseHandling;
jObjectReader.DateTimeZoneHandling=reader.DateTimeZoneHandling;
jObjectReader.FloatParseHandling=reader.FloatParseHandling;
jObjectReader.MaxDepth=reader.MaxDepth;
jObjectReader.SupportMultipleContent=reader.SupportMultipleContent;
返回读卡器;
}
}

看起来有很多方法可以做到这一点,但有一件事我不想做,那就是必须修改我的所有数据对象,以了解它们应该如何序列化/反序列化

一种方法是以DefaultContractResolver的其他人所做的(但仍然没有做我需要做的)为例,修改它们以填充只读字段

这是我想要序列化/反序列化的类

public class CannotDeserializeThis
{
    private readonly IList<User> _users = new List<User>();
    public virtual IEnumerable<User> Users => _users.ToList().AsReadOnly();

    public void AddUser(User user)
    {
        _users.Add(user);
    }
}
DefaultContractResolver正在查找相应的私有支持字段,从该字段创建属性,并将其重命名为公共只读属性


不过,这是一个惯例。您的支持字段以下划线开头,并且是公共属性的小写版本。对于我们正在使用的大多数代码,这是一个安全的假设。(例如,“用户”->“\u用户”或“其他属性名称”->“\u其他属性名称”)

您是否尝试了
var list=new object[]{……};var ser=JsonConvert.serialized对象(列表,格式化,缩进)?@Kiquenet:我不是直接反序列化列表,我是反序列化一个将列表作为只读属性的对象。@Nelin什么对你有用?我的问题不仅仅是通过私有setter设置某些内容。虽然您的解决方案适用于float或double等基本类型,但它不适用于IEnumerabl
public class CannotDeserializeThis
{
    private readonly IList<User> _users = new List<User>();
    public virtual IEnumerable<User> Users => _users.ToList().AsReadOnly();

    public void AddUser(User user)
    {
        _users.Add(user);
    }
}
public class ReadonlyJsonDefaultContractResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var prop = base.CreateProperty(member, memberSerialization);
        if (!prop.Writable)
        {
            var property = member as PropertyInfo;
            if (property != null)
            {
                var hasPrivateSetter = property.GetSetMethod(true) != null;
                prop.Writable = hasPrivateSetter;

                if (!prop.Writable)
                {
                    var privateField = member.DeclaringType.GetRuntimeFields().FirstOrDefault(x => x.Name.Equals("_" + Char.ToLowerInvariant(prop.PropertyName[0]) + prop.PropertyName.Substring(1)));

                    if (privateField != null)
                    {
                        var originalPropertyName = prop.PropertyName;
                        prop = base.CreateProperty(privateField, memberSerialization);
                        prop.Writable = true;
                        prop.PropertyName = originalPropertyName;
                        prop.UnderlyingName = originalPropertyName;
                        prop.Readable = true;
                    }
                }
            }
        }

        return prop;
    }
}