@ИгорьОрлов的答案适用于仅能由JSON.net直接实例化的类型(因为[JsonConstructor]
和/或在构造函数参数上直接使用[JsonProperty]
),但是当JSON.net已经缓存了要使用的转换器时,覆盖contract.Converter = null
不起作用。
(如果JSON.NET使用不可变类型来指示数据和配置何时不再可变,那就不会有这个问题,叹气)
在我的情况下,我做了以下操作:
- 实现自定义的
JsonConverter<T>
(其中T
是我的DTO的基类)。
- 定义一个
DefaultContractResolver
子类,覆盖ResolveContractConverter
以仅为基类返回我的自定义JsonConverter
。
详细说明,以及示例:
假设我有这些不可变的DTO,它们代表远程文件系统(因此有DirectoryDto
和FileDto
,它们都继承自FileSystemDto
,就像DirectoryInfo
和FileInfo
从System.IO.FileSystemInfo
派生出来的一样):
public enum DtoKind
{
None = 0,
File,
Directory
}
public abstract class FileSystemDto
{
protected FileSystemDto( String name, DtoKind kind )
{
this.Name = name ?? throw new ArgumentNullException(nameof(name));
this.Kind = kind;
}
[JsonProperty( "name" )]
public String Name { get; }
[JsonProperty( "kind" )]
public String Kind { get; }
}
public class FileDto : FileSystemDto
{
[JsonConstructor]
public FileDto(
[JsonProperty("name" )] String name,
[JsonProperty("length")] Int64 length,
[JsonProperty("kind") ] DtoKind kind
)
: base( name: name, kind: kind )
{
if( kind != DtoKind.File ) throw new InvalidOperationException( "blargh" );
this.Length = length;
}
[JsonProperty( "length" )]
public Int64 Length { get; }
}
public class DirectoryDto : FileSystemDto
{
[JsonConstructor]
public FileDto(
[JsonProperty("name")] String name,
[JsonProperty("kind")] DtoKind kind
)
: base( name: name, kind: kind )
{
if( kind != DtoKind.Directory ) throw new InvalidOperationException( "blargh" );
}
}
假设我有一个JSON数组
FileSystemDto
:
[
{ "name": "foo.txt", "kind": "File", "length": 12345 },
{ "name": "bar.txt", "kind": "File", "length": 12345 },
{ "name": "subdir", "kind": "Directory" },
]
我想要使用Json.net将其反序列化为List<FileSystemDto>
...
因此,定义一个DefaultContractResolver
的子类(或者如果您已经有一个解析器实现,则子类化(或组合)该解析器),并覆盖ResolveContractConverter
:
public class MyContractResolver : DefaultContractResolver
{
protected override JsonConverter? ResolveContractConverter( Type objectType )
{
if( objectType == typeof(FileSystemDto) )
{
return MyJsonConverter.Instance;
}
else if( objectType == typeof(FileDto ) )
{
}
else if( objectType == typeof(DirectoryDto) )
{
}
return base.ResolveContractConverter( objectType );
}
}
然后实现MyJsonConverter
:
public class MyJsonConverter : JsonConverter<FileSystemDto>
{
public static MyJsonConverter Instance { get; } = new MyJsonConverter();
private MyJsonConverter() {}
public override FileSystemDto? ReadJson( JsonReader reader, Type objectType, FileSystemDto? existingValue, Boolean hasExistingValue, JsonSerializer serializer )
{
if( reader.TokenType == JsonToken.Null ) return null;
if( objectType == typeof(FileSystemDto) )
{
JObject jsonObject = JObject.Load( reader );
if( jsonObject.Property( "kind" )?.Value is JValue jv && jv.Value is String kind )
{
if( kind == "File" )
{
return jsonObject.ToObject<FileDto>( serializer );
}
else if( kind == "Directory" )
{
return jsonObject.ToObject<DirectoryDto>( serializer );
}
}
}
return null;
}
}
然后,要进行反序列化,请使用正确设置了ContractResolver
的JsonSerializer
实例,例如:
public static IReadOnlyList<FileSystemDto> DeserializeFileSystemJsonArray( String json )
{
JsonSerializer jss = new JsonSerializer()
{
ContractResolver = new KuduDtoContractResolver()
};
using( StringReader strRdr = new StringReader( json ) )
using( JsonTextReader jsonRdr = new JsonTextReader( strRdr ) )
{
List<FileSystemDto>? list = jss.Deserialize< List<FileSystemDto> >( jsonRdr );
return list;
}
}