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;
}
}