gpt4 book ai didi

c# - 为什么 XmlSerializer 在 .Net Core 中无法序列化枚举值,但在 .NET Framework 中工作正常

转载 作者:行者123 更新时间:2023-12-02 00:02:26 25 4
gpt4 key购买 nike

摘要

.NET Core 应用无法对包含枚举值的对象进行 XML 序列化,而 .NET Framework (4.7.2) 则可以成功。这是已知的重大更改吗?如果是,我该如何解决它?

代码示例

以下控制台应用程序在 .NET Framework 4.7.2 项目中不会引发异常:

public enum MyEnum
{
One,
}

public class ValueContainer
{
public object Value;
}
class Program
{
static void Main(string[] args)
{
XmlSerializer newSerializer = XmlSerializer.FromTypes(
new[] { typeof(ValueContainer)})[0];

var instance = new ValueContainer();
instance.Value = MyEnum.One;

using (var memoryStream = new MemoryStream())
{
newSerializer.Serialize(memoryStream, instance);
}
}
}

.NET Core 3.0 控制台应用程序中的完全相同的代码在调用 Serialize 时抛出以下异常:

System.InvalidOperationException
HResult=0x80131509
Message=There was an error generating the XML document.
Source=System.Private.Xml
StackTrace:
at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
at System.Xml.Serialization.XmlSerializer.Serialize(Stream stream, Object o, XmlSerializerNamespaces namespaces)
at System.Xml.Serialization.XmlSerializer.Serialize(Stream stream, Object o)
at CoreXml.Program.Main(String[] args) in C:\Users\vchel\source\repos\CoreXml\CoreXml\Program.cs:line 28

Inner Exception 1:
InvalidOperationException: The type CoreXml.MyEnum may not be used in this context.

我的代码是否做错了什么?这是 .NET Framework 和 .NET Core 之间的重大变化吗?

有解决办法吗?

更新

我应该指出,在 .NET 4.7.2 中序列化时,我得到以下(所需的)值输出:

 <Value xsi:type="xsd:int">0</Value>

我希望为 .NET Core 提出的任何解决方案也能输出相同的 XML,因为我需要保持与现有文件和不使用 .NET Standard 的旧版本应用程序的兼容性。

更新2

我应该在最初的问题中包含这些信息,但现在我正在尝试实现答案,我发现有一些我一开始没有想到的要求。

首先,被序列化的对象也在逻辑中使用,而逻辑取决于存储在值中的对象是枚举。因此,将值永久转换为整数(例如通过在 setter 中进行转换)将影响应用程序的逻辑,因此我无法这样做。

其次,尽管我的示例已被简化以显示 .NET Framework 和 .NET Core 之间的差异,但实际应用程序仍使用许多枚举。因此,解决方案应该允许使用多个枚举值。

最佳答案

此重大更改是由于 XmlSerializationWriter.WriteTypedPrimitive(string name, string ns, object o, bool xsiType) 中的实现差异造成的.NET Core 和 .NET Framework 之间。

这可以在以下两个演示 fiddle 中看到:

  1. .NET Core 3.1.0 ,它会抛出异常,如下所示:

    System.InvalidOperationException: There was an error generating the XML document.
    ---> System.InvalidOperationException: The type MyEnum may not be used in this context.
    at System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(String name, String ns, Object o, Boolean xsiType)
  2. .NET Framework 4.7.3460.0 ,它序列化一个new ValueContainer { Value = MyEnum.One },如下所示:

    <ValueContainer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Value xsi:type="xsd:int">0</Value>
    </ValueContainer>

    请注意,生成 XML 时,不包含有关 Value 中存在的特定 enum 类型的信息,而仅包含基础类型 int显示在xsi:type中属性。

那么,差异在哪里呢?完整框架引用源码可见here ,并开始:

    protected void WriteTypedPrimitive(string name, string ns, object o, bool xsiType) {
string value = null;
string type;
string typeNs = XmlSchema.Namespace;
bool writeRaw = true;
bool writeDirect = false;
Type t = o.GetType();
bool wroteStartElement = false;

switch (Type.GetTypeCode(t)) {
case TypeCode.String:
value = (string)o;
type = "string";
writeRaw = false;
break;
case TypeCode.Int32:
value = XmlConvert.ToString((int)o);
type = "int";
break;

假设传入的object o实际上是一个装箱的Enum.One,那么Type.GetTypeCode(Type type)返回 TypeCode适用于枚举的底层类型,此处为TypeCode.Int32,从而成功序列化您的值。

当前.Net core引用源是here表面上看起来很相似:

    protected void WriteTypedPrimitive(string name, string ns, object o, bool xsiType)
{
string value = null;
string type;
string typeNs = XmlSchema.Namespace;
bool writeRaw = true;
bool writeDirect = false;
Type t = o.GetType();
bool wroteStartElement = false;

switch (t.GetTypeCode())
{
case TypeCode.String:
value = (string)o;
type = "string";
writeRaw = false;
break;
case TypeCode.Int32:
value = XmlConvert.ToString((int)o);
type = "int";
break;

但是等等 - 这个方法 t.GetTypeCode() 是什么? Type 上没有实例方法 GetTypeCode()所以它一定是某种扩展方法。但是哪里?快速搜索引用源发现至少三个不同的、不一致的 public static TypeCode GetTypeCode(this Type type) 方法:

  1. System.Runtime.Serialization.TypeExtensionMethods.GetTypeCode(this Type type) .

  2. System.Dynamic.Utils.TypeExtensions.GetTypeCode(this Type type) .

  3. System.Xml.Serialization.TypeExtensionMethods.GetTypeCode(this Type type) .

    由于System.Xml.SerializationXmlSerializationWriter的命名空间,我相信这就是所使用的。并且它不会调用Type.GetTypeCode():

    public static TypeCode GetTypeCode(this Type type)
    {
    if (type == null)
    {
    return TypeCode.Empty;
    }
    else if (type == typeof(bool))
    {
    return TypeCode.Boolean;
    }
    else if (type == typeof(char))
    {
    return TypeCode.Char;
    }
    else if (type == typeof(sbyte))
    {
    return TypeCode.SByte;
    }
    else if (type == typeof(byte))
    {
    return TypeCode.Byte;
    }
    else if (type == typeof(short))
    {
    return TypeCode.Int16;
    }
    else if (type == typeof(ushort))
    {
    return TypeCode.UInt16;
    }
    else if (type == typeof(int))
    {
    return TypeCode.Int32;
    }
    else if (type == typeof(uint))
    {
    return TypeCode.UInt32;
    }
    else if (type == typeof(long))
    {
    return TypeCode.Int64;
    }
    else if (type == typeof(ulong))
    {
    return TypeCode.UInt64;
    }
    else if (type == typeof(float))
    {
    return TypeCode.Single;
    }
    else if (type == typeof(double))
    {
    return TypeCode.Double;
    }
    else if (type == typeof(decimal))
    {
    return TypeCode.Decimal;
    }
    else if (type == typeof(DateTime))
    {
    return TypeCode.DateTime;
    }
    else if (type == typeof(string))
    {
    return TypeCode.String;
    }
    else
    {
    return TypeCode.Object;
    }
    }

    因此,当传递 enum 类型时,将返回 TypeCode.Object

System.Type.GetTypeCode(Type t) 替换为 System.Xml.Serialization.TypeExtensionMethods.GetTypeCode(this Type type) 是重大更改,即导致序列化失败。

所有这些都引出了一个问题,此重大更改是错误还是错误修复?

XmlSerializer 专为可序列化对象的往返而设计:它通常拒绝序列化任何无法在不丢失数据的情况下反序列化的类型。但就您而言,数据正在丢失,因为 enum 值正在降级为整数值。所以这种行为改变可能是有意的。不过,您可以提出一个问题 here询问重大更改是否是有意为之。

为了避免异常,您应该使用 [XmlInclude(typeof(TEnum))] 正确声明所有预期的 enum 类型(和其他类型)。 ValueContainer 上的属性:

[XmlInclude(typeof(MyEnum)), XmlInclude(typeof(SomeOtherEnum)), XmlInclude(typeof(SomeOtherClass)) /* Include all other expected custom types here*/]
public class ValueContainer
{
public object Value;
}

这是使用 XmlSerializer 序列化多态成员的预期方法,并确保类型信息是往返的。它适用于 .NET Core 和 .NET Full Framework。有关相关问题,请参阅 Serializing a class with a generic Enum that can be different Enum types Using XmlSerializer to serialize derived classes

演示 fiddle #3 here .

this answer 中建议的解决方法通过 Eldar也可以避免异常,但将 enum 转换为 int 会导致类型信息丢失。

关于c# - 为什么 XmlSerializer 在 .Net Core 中无法序列化枚举值,但在 .NET Framework 中工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59484053/

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