gpt4 book ai didi

c# - 当具体类包含其他接口(interface)时如何反序列化接口(interface)集合

转载 作者:行者123 更新时间:2023-12-04 17:47:11 31 4
gpt4 key购买 nike

我目前面临的情况是,我得到了一个我无法修改的 json 文件,我希望生成的反序列化类对于设计目的是通用的。

首先是我的界面:

public interface IJobModel
{
string ClientBaseURL { get; set; }
string UserEmail { get; set; }
ExportType Type { get; set; }
List<IItemModel> Items { get; set; }
}

public interface IItemModel
{
string Id { get; set; }
string ImageSize { get; set; }
string ImagePpi { get; set; }
List<ICamSettings> CamSettings { get; set; }
}

public interface ICamSettings
{
string FileName { get; set; }
}

然后这是我为解决我的问题而设计的代码:

public class ThumbnailJobModel : IJobModel
{
[JsonProperty( "clientBaseURL" )]
public string ClientBaseURL { get; set; }

[JsonProperty( "userEmail" )]
public string UserEmail { get; set; }

[JsonProperty( "type" )]
[JsonConverter( typeof( TypeConverter ) )]
public ExportType Type { get; set; }

[JsonProperty( "items" )]
[JsonConverter( typeof( ConcreteConverter<List<IItemModel>, List<Item>>
) )]
public List<IItemModel> Items { get; set; }

public ThumbnailJobModel()
{
Type = ExportType.Thumbnails;
Items = new List<IItemModel>();
}

public class Item : IItemModel
{
[JsonProperty( "id" )]
public string Id { get; set; }

[JsonProperty( "imageSize" )]
public string ImageSize { get; set; }

[JsonProperty( "imagePpi" )]
public string ImagePpi { get; set; }

[JsonProperty( "shoots" )]
//[JsonConverter( typeof( CamSettingsConverter ) )]
[JsonConverter( typeof( ConcreteConverter<List<ICamSettings>,
List<ShootSettings>> ) )]
public List<ICamSettings> CamSettings { get; set; }

public Item()
{
CamSettings = new List<ICamSettings>();
}
}

public class ShootSettings : ICamSettings
{
[JsonProperty( "orientation" )]
[JsonConverter( typeof( OrientationConverter ) )]
public Orientation Orientation { get; set; }

[JsonProperty( "clothShape" )]
[JsonConverter( typeof( ClothShapeConverter ) )]
public Shape Shape { get; set; }

[JsonProperty( "fileName" )]
public string FileName { get; set; }

public ShootSettings()
{
Orientation = Orientation.Perspective;
Shape = Shape.Folded;
FileName = null;
}
}

public enum Orientation
{
Perspective = 0,
Oblique = 1,
Front = 2,
Back = 3,
Left = 4,
Right = 5,
Up = 6,
Down = 7
}

public enum Shape
{
Folded = 0,
Hanger = 1,
Mannequin = 2
}

public class ConcreteConverter<I, T> : JsonConverter
{
public override bool CanConvert( Type objectType )
{
return typeof( I ) == objectType;
}

public override object ReadJson( JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer )
{
return serializer.Deserialize<T>( reader );
}

public override void WriteJson( JsonWriter writer,
object value, JsonSerializer serializer )
{
throw new NotImplementedException();
}
}

public class OrientationConverter : JsonConverter
{
public override object ReadJson( JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer )
{
string enumString = (string)reader.Value;

return Enum.Parse( typeof( Orientation ), enumString, true );
}

public override bool CanConvert( Type objectType )
{
return objectType == typeof( string );
}

public override void WriteJson( JsonWriter writer, object value,
JsonSerializer serializer )
{
throw new NotImplementedException();
}
}

public class ClothShapeConverter : JsonConverter
{
public override object ReadJson( JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer )
{
var enumString = (string)reader.Value;

return Enum.Parse( typeof( Shape ), enumString, true );
}

public override bool CanConvert( Type objectType )
{
return objectType == typeof( string );
}

public override void WriteJson( JsonWriter writer, object value,
JsonSerializer serializer )
{
throw new NotImplementedException();
}
}

public class TypeConverter : JsonConverter
{
public override object ReadJson( JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer )
{
return ExportType.Thumbnails;
}

public override bool CanConvert( Type objectType )
{
return objectType == typeof( string );
}

public override void WriteJson( JsonWriter writer, object value,
JsonSerializer serializer )
{
throw new NotImplementedException();
}
}

public static void HandleDeserializationError( object sender,
ErrorEventArgs errorArgs )
{
errorArgs.ErrorContext.Handled = true;
var currentObj = errorArgs.CurrentObject as ShootSettings;

if ( currentObj == null ) return;

currentObj.Orientation = Orientation.Perspective;
currentObj.Shape = Shape.Folded;
}
}

如您所见,IItemModel 界面中有一个ICamSettings 列表。

我尝试将此 json 反序列化到我的 ThumbnailJobModel 类中:

{
"clientBaseURL":"https://clientName.fr",
"userEmail":"myName@gmail.com",
"items":[
{
"id":"11913",
"imageSize":"1280,720",
"imagePpi":"72",
"shoots":[
{
"fileName":"front1.png",
"orientation":"front",
"clothShape":"hanger"
},
{
"fileName":"folded1.png",
"orientation":"front",
"clothShape":"folded"
},
{
"fileName":"right1.png",
"orientation":"right",
"clothShape":"hanger"
}
]
},
{
"id":"2988",
"imageSize":"1280,720",
"imagePpi":"",
"shoots":[
{
"fileName":"perspective1.png",
"orientation":"perspective"
}
]
}
]
}

我这样反序列化我的 json :

//Read the job config
string jobConfig = File.ReadAllText( jsonConfigPath );
IJobModel m_jobModel = JsonConvert.DeserializeObject<ThumbnailJobModel>(
jobConfig );

并抛出以下异常:

Exception : Error setting value to 'CamSettings' on 
'IWD.Screenshoter.Job.ThumbnailJobModel+Item'.
Stack :
at Newtonsoft.Json.Serialization.DynamicValueProvider.SetValue
(System.Object target, System.Object value) [0x00000] in <filename
unknown>:0
at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue
(Newtonsoft.Json.Serialization.JsonProperty property,
Newtonsoft.Json.JsonConverter propertyConverter,
Newtonsoft.Json.Serialization.JsonContainerContract containerContract,
Newtonsoft.Json.Serialization.JsonProperty containerProperty,
Newtonsoft.Json.JsonReader reader, System.Object target) [0x00000] in
<filename unknown>:0
at
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject
(System.Object newObject, Newtonsoft.Json.JsonReader reader,
Newtonsoft.Json.Serialization.JsonObjectContract contract,
Newtonsoft.Json.Serialization.JsonProperty member, System.String id)
[0x00000] in <filename unknown>:0

老实说,我不明白我做错了什么,我希望有人能够对此有所启发。

最佳答案

你的基本问题是你的 ConcreteConverter<I, T>旨在将声明为接口(interface)的内容反序列化为具体类型——例如IItemModel作为Item - 但你没有以那种方式使用它。您正在使用它将具体的接口(interface)列表反序列化为具体类型的具体列表,例如:

[JsonProperty( "items" )]
[JsonConverter( typeof( ConcreteConverter<List<IItemModel>, List<Item>>) )]
public List<IItemModel> Items { get; set; }

相反,您应该将转换器应用于 Items项目CamSettings使用 JsonPropertyAttribute.ItemConverterType 的集合像这样:

public class ThumbnailJobModel : IJobModel
{
[JsonProperty("items", ItemConverterType = typeof(ConcreteConverter<IItemModel, Item>))]
public List<IItemModel> Items { get; set; }

public class Item : IItemModel
{
[JsonProperty("shoots", ItemConverterType = typeof(ConcreteConverter<ICamSettings, ShootSettings>))]
public List<ICamSettings> CamSettings { get; set; }

这应该可以修复异常。但是,还有其他建议:

  • 在几个转换器中,您没有实现 WriteJson() .如果你想使用默认序列化,你可以override CanWrite and return false .

  • 请重命名 TypeConverterExportTypeConverter . TypeConverter已用于 something else .

  • OrientationConverterClothShapeConverter是不必要的,内置的 StringEnumConverter 会将任何枚举序列化和反序列化为字符串。

    如果您希望为数字枚举值抛出异常,您可以将其子类化为 StrictStringEnumConverter并设置 AllowIntegerValues = false :

    public class StrictStringEnumConverter : StringEnumConverter
    {
    public StrictStringEnumConverter() { this.AllowIntegerValues = false; }
    }

    你也可以制作ExportTypeConverter继承自 StringEnumConverter以获得所需的序列化行为。

  • ConcreteConverterT应该是 I 的具体实现, 你可以添加一个 where确保该类型的用户不会意外反转通用参数的约束:

    public class ConcreteConverter<IInterface, TConcrete> : JsonConverter where TConcrete : IInterface
    {
    }

    我还将通用参数重命名为更有意义的内容。

  • 在几个转换器中,您覆盖了 CanConvert(Type)并测试传入类型是 string , 其中string是序列化到文件的类型:

    public override bool CanConvert( Type objectType )
    {
    return objectType == typeof( string );
    }

    当直接由属性应用时,CanConvert()永远不会被调用。当通过设置应用时,在序列化期间 objectType是即将被序列化的对象的实际类型。当通过设置应用时,在反序列化过程中 objectType是其值即将被反序列化的成员的声明类型。它永远不是文件中的类型。因此在 ExportTypeConverter应该这样写:

    public override bool CanConvert(Type objectType)
    {
    return objectType == typeof(ExportType);
    }

    或者,由于转换器仅适用于属性,您可以抛出 NotImplementedException .

  • 我看不出有任何理由嵌套像 Item 这样的模型里面ThumbnailJobModel .对我来说,这只会导致额外的复杂性。您可以改为将它们设为非公开。但这只是见仁见智。

将所有这些放在一起,您的代码应该类似于:

public interface IJobModel
{
string ClientBaseURL { get; set; }
string UserEmail { get; set; }
ExportType Type { get; set; }
List<IItemModel> Items { get; set; }
}

public interface IItemModel
{
string Id { get; set; }
string ImageSize { get; set; }
string ImagePpi { get; set; }
List<ICamSettings> CamSettings { get; set; }
}

public interface ICamSettings
{
string FileName { get; set; }
}

public enum ExportType
{
Thumbnails,
}

public class ThumbnailJobModel : IJobModel
{
[JsonProperty("clientBaseURL")]
public string ClientBaseURL { get; set; }

[JsonProperty("userEmail")]
public string UserEmail { get; set; }

[JsonProperty("type")]
[JsonConverter(typeof(ExportTypeConverter))]
public ExportType Type { get; set; }

[JsonProperty("items", ItemConverterType = typeof(ConcreteConverter<IItemModel, Item>))]
public List<IItemModel> Items { get; set; }

public ThumbnailJobModel()
{
Type = ExportType.Thumbnails;
Items = new List<IItemModel>();
}

public class Item : IItemModel
{
[JsonProperty("id")]
public string Id { get; set; }

[JsonProperty("imageSize")]
public string ImageSize { get; set; }

[JsonProperty("imagePpi")]
public string ImagePpi { get; set; }

[JsonProperty("shoots", ItemConverterType = typeof(ConcreteConverter<ICamSettings, ShootSettings>))]
public List<ICamSettings> CamSettings { get; set; }

public Item()
{
CamSettings = new List<ICamSettings>();
}
}

public class ShootSettings : ICamSettings
{
[JsonProperty("orientation")]
[JsonConverter(typeof(StrictStringEnumConverter))]
public Orientation Orientation { get; set; }

[JsonProperty("clothShape")]
[JsonConverter(typeof(StrictStringEnumConverter))]
public Shape Shape { get; set; }

[JsonProperty("fileName")]
public string FileName { get; set; }

public ShootSettings()
{
Orientation = Orientation.Perspective;
Shape = Shape.Folded;
FileName = null;
}
}

public enum Orientation
{
Perspective = 0,
Oblique = 1,
Front = 2,
Back = 3,
Left = 4,
Right = 5,
Up = 6,
Down = 7
}

public enum Shape
{
Folded = 0,
Hanger = 1,
Mannequin = 2
}

public class ConcreteConverter<IInterface, TConcrete> : JsonConverter where TConcrete : IInterface
{
public override bool CanConvert(Type objectType)
{
return typeof(IInterface) == objectType;
}

public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TConcrete>(reader);
}

public override bool CanWrite { get { return false; } }

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}

public class ExportTypeConverter : StringEnumConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
reader.Skip(); // Skip anything at the current reader's position.
return ExportType.Thumbnails;
}

public override bool CanConvert(Type objectType)
{
return objectType == typeof(ExportType);
}
}

public class StrictStringEnumConverter : StringEnumConverter
{
public StrictStringEnumConverter() { this.AllowIntegerValues = false; }
}

public static void HandleDeserializationError(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs errorArgs)
{
errorArgs.ErrorContext.Handled = true;
var currentObj = errorArgs.CurrentObject as ShootSettings;

if (currentObj == null) return;

currentObj.Orientation = Orientation.Perspective;
currentObj.Shape = Shape.Folded;
}
}

sample 加工 .Net fiddle .

关于c# - 当具体类包含其他接口(interface)时如何反序列化接口(interface)集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47939878/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com