F.V*_*.V. 18 c# jsonserializer system.text.json
我有以下简单的课程:
public abstract class GitObject
{
public Repository Repository { get; set; }
public abstract string Serialize();
public abstract void Deserialize(string data);
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository, string data = null)
{
if (data != null) Data = File.ReadAllText(data);
Repository = repository;
}
public override string Serialize()
{
return JsonSerializer.Serialize(this);
}
public override void Deserialize(string data)
{
Blob blobData = JsonSerializer.Deserialize<Blob>(data);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我知道可能还有很大的改进空间(我很高兴听到这一点)。但是,该方法Deserialize
给了我错误
Each parameter in the deserialization constructor on type 'CustomGit.Repository'
must bind to an object property or field on deserialization. Each parameter name must
match with a property or field on the object. The match can be case-insensitive.
Run Code Online (Sandbox Code Playgroud)
为了测试此方法是否按预期工作,我使用此方法(这也会引发错误)
FileInfo file = new FileInfo(Path.Combine(repository.GitDirectory.FullName, "code.txt"));
GitObject.Blob firstBlob = new GitObject.Blob(repository, file.FullName);
var json = firstBlob.Serialize();
GitObject.Blob secondBlob = new GitObject.Blob(repository);
secondBlob.Deserialize(json);
Run Code Online (Sandbox Code Playgroud)
我做错了什么以及我一般应该改变什么?
dbc*_*dbc 25
您遇到了两个与使用参数化构造函数反序列化类型相关的独立问题。如文档页面How to use immutable types and non-public accessors with System.Text.Json中所述:
System.Text.Json
可以使用公共参数化构造函数,这使得反序列化不可变类或结构成为可能。对于一个类,如果唯一的构造函数是参数化构造函数,则将使用该构造函数。对于结构体或具有多个构造函数的类,通过应用属性来指定要使用的构造函数[JsonConstructor]
。当不使用该属性时,始终使用公共无参数构造函数(如果存在)。该属性只能与公共构造函数一起使用。...
参数化构造函数的参数名称必须与属性名称和类型匹配。匹配不区分大小写,即使您使用
[JsonPropertyName]
重命名属性,构造函数参数也必须与实际属性名称匹配。 [1]
你的第一个问题是类型Repository
。您没有在问题中显示它,但我认为它看起来像这样:
public class Repository
{
public Repository(string gitDirectory) => this.GitDirectory = new DirectoryInfo(gitDirectory);
[JsonConverter(typeof(DirectoryInfoConverter))]
public DirectoryInfo GitDirectory { get; }
}
public class DirectoryInfoConverter : JsonConverter<DirectoryInfo>
{
public override DirectoryInfo Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
new DirectoryInfo(reader.GetString());
public override void Write(Utf8JsonWriter writer, DirectoryInfo value, JsonSerializerOptions options) =>
writer.WriteStringValue(value.ToString());
}
Run Code Online (Sandbox Code Playgroud)
如果是这样,你这里的问题是要么对应的构造函数参数的名称GitDirectory
与属性名称不同,要么参数的类型不同。
演示小提琴#1在这里。
要解决此问题,您必须:
添加一个公共无参数构造函数并使其Repository
可变(即添加一个 setter GitDirectory
),或者
添加一个构造函数,其参数与属性的类型和名称相同GitDirectory
,并用 标记它[JsonConstructor]
。
采用选项 #2,您的Repository
类型现在应如下所示:
public class Repository
{
public Repository(string gitDirectory) => this.GitDirectory = new DirectoryInfo(gitDirectory);
[JsonConstructor]
public Repository(DirectoryInfo gitDirectory) => this.GitDirectory = gitDirectory ?? throw new ArgumentNullException(nameof(gitDirectory));
[JsonConverter(typeof(DirectoryInfoConverter))]
public DirectoryInfo GitDirectory { get; }
}
Run Code Online (Sandbox Code Playgroud)
现在Respository
将成功反序列化。演示小提琴#2在这里。
但是,您现在将遇到第二个问题,即该Blob
类型也不会往返。在这种情况下,Blob
确实有一个唯一的参数化构造函数,其参数名称和类型与属性精确对应 - 但其中之一的语义data
完全不同:
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository, string data = null)
{
if (data != null)
Data = File.ReadAllText(data);
Repository = repository;
}
Run Code Online (Sandbox Code Playgroud)
属性Data
对应于文件的文本内容,而参数data
对应于文件的文件名。因此,当反序列化时,Blob
您的代码将尝试读取名称与文件内容相同的文件,但会失败。
在我看来,这种不一致是糟糕的编程风格,并且可能会让其他开发人员以及 System.Text.Json 感到困惑。Blob
相反,请考虑添加工厂方法以从文件或文件内容创建,并删除相应的构造函数参数。因此你的Blob
应该看起来像:
public class Blob : GitObject
{
public string Data { get; set; }
public Blob(Repository repository) => this.Repository = repository ?? throw new ArgumentNullException(nameof(repository));
public static Blob CreateFromDataFile(Repository repository, string dataFileName) =>
new Blob(repository)
{
Data = File.ReadAllText(dataFileName),
};
public static Blob CreateFromDataConents(Repository repository, string data) =>
new Blob(repository)
{
Data = data,
};
public override string Serialize() => JsonSerializer.Serialize(this);
public override void Deserialize(string data)
{
// System.Text.Json does not have a Populate() method so we have to do it manually, or via a tool like AutoMapper
Blob blobData = JsonSerializer.Deserialize<Blob>(data);
this.Repository = blobData.Repository;
this.Data = blobData.Data;
}
}
Run Code Online (Sandbox Code Playgroud)
您将按如下方式构建和往返它:
var firstBlob = GitObject.Blob.CreateFromDataFile(repository, file.FullName);
var json = firstBlob.Serialize();
var secondBlob = new GitObject.Blob(repository);
secondBlob.Deserialize(json);
Run Code Online (Sandbox Code Playgroud)
最终工作演示小提琴在这里。
[1] 该文档于 2023 年更新。在提出这个问题时,文档仅说明
参数化构造函数的参数名称必须与属性名称匹配。