我已经和JSON.net合作了一段时间.我已经编写了自定义转换器和自定义合同解析器(通常来自修改SO和Newtonsoft网站上的示例),它们工作正常.
除了例子之外,我面临的挑战是,我什么时候应该使用其中一个(或两个)进行处理.根据我自己的经验,我基本上已经确定合同解决方案更简单了,所以如果我可以用他们做我需要的东西,我会这样做; 否则,我使用自定义JsonConverters.但是,我进一步知道两者有时一起使用,因此概念变得更加不透明.
问题:
我有大约以下图片:
public class Foo
{
public Foo(Bar bar, String x, String y)
{
this.Bar = bar;
this.X = x;
this.Y = y;
}
[JsonIgnore]
public Bar Bar { get; private set; }
public String X { get; private set; }
public String Y { get; private set; }
}
public class Bar
{
public Bar(String z)
{
this.Z = z;
}
public String Z { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)
我希望以某种方式在反序列化期间将类型为Bar的对象传递给Foo类型的构造函数,即:
var bar = new Bar("Hello world");
var x = …Run Code Online (Sandbox Code Playgroud) 我有一个我不用多个构造函数控制的类型,相当于这个:
public class MyClass
{
private readonly string _property;
private MyClass()
{
Console.WriteLine("We don't want this one to be called.");
}
public MyClass(string property)
{
_property = property;
}
public MyClass(object obj) : this(obj.ToString()) {}
public string Property
{
get { return _property; }
}
}
Run Code Online (Sandbox Code Playgroud)
现在,当我尝试反序列化它时,将调用私有无参数构造函数,并且永远不会设置该属性.考试:
[Test]
public void MyClassSerializes()
{
MyClass expected = new MyClass("test");
string output = JsonConvert.SerializeObject(expected);
MyClass actual = JsonConvert.DeserializeObject<MyClass>(output);
Assert.AreEqual(expected.Property, actual.Property);
}
Run Code Online (Sandbox Code Playgroud)
给出以下输出:
We don't want this one to be called.
Expected: "test"
But …Run Code Online (Sandbox Code Playgroud) Using Json.net, deserializing a type that contains a Tuple<...> doesn't work (serialization works, but deserialization doesn't):
[TestMethod]
public void Test()
{
var orig = new TupleHolder("what????", true);
var json = JsonConvert.SerializeObject(orig);
Assert.AreEqual("{\"Tup\":{\"Item1\":\"what????\",\"Item2\":true}}", json);
// great! serialization works like a charm! now let's test deserialization:
var dupl = JsonConvert.DeserializeObject<TupleHolder>(json);
Assert.AreEqual("ZZZ", dupl.Tup.Item1); // pass! but it should be "what????"... what????
Assert.AreEqual(false, dupl.Tup.Item2); // pass! but it should be "true", right???
Assert.AreEqual(orig.Tup.Item1, dupl.Tup.Item1); // fail!
Assert.AreEqual(orig.Tup.Item2, dupl.Tup.Item2); // fail!
}
public class TupleHolder …Run Code Online (Sandbox Code Playgroud) c# serialization json.net deserialization json-deserialization
在 .NET 5.0 上使用 C# 9,我有一堆记录类型,如下所示:
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened)
{
public SomethingHappenedEvent(object theThing, string whatHappened)
: this(Guid.NewGuid(), theThing, whatHappened)
{ }
}
Run Code Online (Sandbox Code Playgroud)
正如您所料,它们被序列化并发送到其他地方进行处理。发送者调用双参数构造函数并获得一个新的 Id,但反序列化器需要使用记录声明隐含的“主要”3 参数构造函数。我正在使用 Newtonsoft Json.NET,我当然希望这能奏效:
var record = new SomethingHappenedEvent("roof", "caught fire");
var json = JsonConvert.SerializeObject(record);
var otherSideRecord = JsonConvert.DeserializeObject<SomethingHappenedEvent>(json);
Assert.AreEqual(record, otherSideRecord);
Run Code Online (Sandbox Code Playgroud)
当然不是。它抛出 JsonSerializationException。它找不到正确的构造函数,因为有两个,既不是默认的零参数构造函数,也没有标记 JsonConstructorAttribute。我的问题实际上是“我有什么选择来获得类似的东西?”。这会很棒:
[JsonConstructor]
public record SomethingHappenedEvent(Guid Id, object TheThing, string WhatHappened)
{
public SomethingHappenedEvent(object theThing, string whatHappened)
: this(Guid.NewGuid(), theThing, whatHappened)
{ }
}
Run Code Online (Sandbox Code Playgroud)
但这会尝试将该属性应用于无效的类型。这是 C# 中的语法错误,但显然它适用于 F#。 …
我有一个类,它尝试序列化和反序列化给它的对象,使用 Json.net
public class JsonSerializer
{
public string Serialize(object toSerialaze)
{
return JsonConvert.SerializeObject(toSerialaze);
}
public T Deserialize<T>(string toDeserialaze)
{
return JsonConvert.DeserializeObject<T>(toDeserialaze);
}
}
Run Code Online (Sandbox Code Playgroud)
并给它一个这样的类的对象
public class Isbn
{
private readonly int _groupCode;
private readonly int _publisherCode;
private readonly int _titleCode;
private readonly int _checkCode;
private static readonly Regex Regex = new Regex(@"^\s*\d*\s*-\s*\d*\s*-\s*\d*\s*-\s*\d*\s*$");
public Isbn(int groupCode, int publisherCode, int titleCode, int checkCode)
{
_groupCode = groupCode;
_publisherCode = publisherCode;
_titleCode = titleCode;
_checkCode = checkCode;
}
public Isbn(string isbn)
{
if …Run Code Online (Sandbox Code Playgroud) 我有一个类属性集,并在构造函数中固定,下面的示例是一个Guid.NewGuid()..
理由是只允许类在创建时填充..所以它在构造函数中设置,并且只标记为get;- 工作正常,直到我序列化/反序列化为 Json。
似乎因为没有set;反序列化失败..并且构造函数介入并创建了一个新的guid
我已经尝试过内部、私有和受保护的 set .. 但所有结果都导致属性的再生,我可以让它工作的唯一方法是将 Id 标记为{get;set;},
class Foo
{
public Foo() => Id = Guid.NewGuid().ToString();
public string Id { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
这意味着您可以简单地执行以下操作:
var obj = new Foo();
obj.Id = "Any Old Rubbish";
Run Code Online (Sandbox Code Playgroud)
有没有办法解决这个问题?
短样本:
class Program
{
static void Main()
{
var obj = new Foo();
Console.WriteLine(obj.Id);
var serialize = JsonConvert.SerializeObject(obj);
var deserialize = JsonConvert.DeserializeObject<Foo>(serialize);
Console.WriteLine(deserialize.Id);
Console.ReadLine();
}
}
class Foo
{
public Foo() => …Run Code Online (Sandbox Code Playgroud) 我一直在从事一个需要通过 JSON 文件保存和加载数据的项目。此 JSON 文件包含其他对象的各种列表。然而,当我继续反序列化文件时,会发生这种情况:
System.NotSupportedException:不支持不带无参数构造函数、单一参数化构造函数或用“JsonConstructorAttribute”注释的参数化构造函数的类型的反序列化。
负责反序列化的代码如下:
public void LoadFromJson() {
int userCount = File.ReadAllLines(folder + "/users").Length;
int shopCount = File.ReadAllLines(folder + "/shops").Length;
using (FileStream fileUsers = File.Open(folder + "/users", FileMode.Open, FileAccess.Read)) {
StreamReader srUser = new StreamReader(fileUsers);
for(int i=0; i<userCount; i++){
ListOfUsers.Add(JsonSerializer.Deserialize<User>(srUser.ReadLine()));
}
srUser.Close();
fileUsers.Close();
}
using (FileStream fileShops = File.Open(folder + "/shops", FileMode.Open, FileAccess.Read)){
StreamReader srShops = new StreamReader(fileShops);
for(int i=0; i<shopCount; i++){
ListOfShops.Add(JsonSerializer.Deserialize<Shop>(srShops.ReadLine()));
}
srShops.Close();
fileShops.Close();
}
}
Run Code Online (Sandbox Code Playgroud)
我试图反序列化的类
public abstract class Shop{
public List<Sellable> ShopList { …Run Code Online (Sandbox Code Playgroud) 我正在研究SignalR的一个项目,我有一些我将要通过它的对象.这些对象只在我的后端代码中显式创建,我真的希望能够对它们强制实现不变性和不变量.我遇到了SignalR要求我(好吧,实际上是NewtonSoft.Json)的问题,在我的属性上有一个默认的,无参数的构造函数和公共setter,以便它通过网络序列化和反序列化它们.
这是一个人为的例子:
public class Message{
public string Text {get;set;}
public int Awesomeness {get;set;}
}
Run Code Online (Sandbox Code Playgroud)
我想要的是,更多的是这些内容(它应该只有readonly私有字段和getter-only属性完全不可变,但对于只有POCO而没有方法的东西,足够好)
public class Message {
public string Text {get;private set;}
public int Awesomeness {get;private set;}
public Message( string msg, int awesome){
if (awesome < 1 || awesome > 5){
throw new ArgumentOutOfRangeException("awesome");
}
Text = msg;
Awesomeness = awesome;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我这样做,我的对象不能被SignalR .NET客户端库反序列化.我可以在那里查找一个默认构造函数,然后公开我的setter,但是我必须记住不要在我的代码中使用它们,并确保团队中没有其他人在不理解的情况下使用它们.
我已经开始尝试这样做,将默认构造函数标记为永远不应该显式使用的东西:
[Obsolete("Bad! don't do this!")
public Message(){}
Run Code Online (Sandbox Code Playgroud)
但是我不能仅仅在属性的setter上使用Obsolete属性.
如果我真的想要,我可以从DTO表示中分离出"真实"对象并在它们之间进行转换,但是我真的很兴奋写一堆样板来做这件事并引入另一层.
有什么我忽略的,或者我只需要咬紧牙关并处理它?
我的示例代码非常简单:
using System.Text.Json.Serialization;
using Newtonsoft.Json;
public class C {
public C(string PracticeName) { this.PracticeName = PracticeName; }
public string PracticeName;
}
var x = new C("1");
var json = JsonConvert.SerializeObject(x); // returns "{\"PracticeName\":\"1\"}"
var x1 = JsonConvert.DeserializeObject<C>(json); // correctly builds a C
var x2 = System.Text.Json.Serialization.JsonSerializer.Parse<C>(json);
Run Code Online (Sandbox Code Playgroud)
最后一行引发:
引发异常:System.Text.Json.dll中的“ System.NullReferenceException”对象引用未设置为对象的实例。
我究竟做错了什么 ?
(Note this is on latest .NET Core 3 preview 5 with latest System.Text.Json 4.6.0-preview6.19259.10)
Adding a parameterless constructor prevents the exception however I don't want/need a parameterless constructor and …
我正在使用newtonsoft的json deserialiser,我在包装器中反序列化这个对象:
public class User
{
public string Username;
public byte[] HashedPassword;
public byte[] Salt;
private bool admin;
public bool Admin
{
get { return admin; }
}
public User(string UsernameArg, byte[] PasswordArg, byte[] SaltArg, bool AdminArg = false)
{
Username = UsernameArg;
HashedPassword = PasswordArg;
Salt = SaltArg;
admin = AdminArg;
}
public override string ToString()
{
return Username;
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的json字符串:
{"Users":[{"Username":"admin","HashedPassword":"password","Salt":"salt","Admin":true}]}
(为了便于阅读,我编辑了哈希密码和盐)
因此,每当我使用JsonConvert.DeserializeObject<UserDatabaseClass>(jsonRead)admin字段读取此内容时返回false.
是不是我误解了这里做了什么,还是我做错了?
可以这样调整JsonSerializerSettings吗?
(我猜不是)但仍有一些希望,因为我对他们的API不是很有经验.
默认情况下,我尝试丢失的所有设置都反序列化为0或导致异常.
例
{
"defaultCaptionLineNum": 6,
"worksheets": {
"0": { "name": "Ws1", "caption_ln": 6 },
"1": { "name": "Ws2", "caption_ln": null },
"2": { "name": "Ws3" },
"3": { "name": "Ws4", "caption_ln": 5 },
}
}
Run Code Online (Sandbox Code Playgroud)
目前默认情况下,Ws3的标题行(caption_ln)计算为0,我希望将其计算为null(如Ws2中所示).
我正在使用
jObject.ToObject<MyClass>(JsonSerializer.Create(settings));
Run Code Online (Sandbox Code Playgroud)
从JSON获取带有工作表(Ws)信息的对象(在没有序列化器的情况下尝试了它)
和任何变化Include,并Ignore在这里不作jObject在反序列化JSON缺少整数不同(与抛误差除外马上遗漏值)
var settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Include,
MissingMemberHandling = MissingMemberHandling.Ignore
};
Run Code Online (Sandbox Code Playgroud)
其他串行器设置似乎无关紧要.
UPD:MyObject MyClass的列表.
public class MyClass
{
public class WorkSheet
{
[JsonProperty("name")]
string wsName;
[JsonProperty("caption_ln")]
int? captionLineNumber;
public WorkSheet(string name, …Run Code Online (Sandbox Code Playgroud)