0

我有一个 ISerializable 复杂对象,我想将其序列化为 XML 文档(我宁愿不更改源代码并添加 XML 序列化属性的节点)。ISerializable 与 BinaryFormatter 配合得很好,但没有标准的方法可以将其序列化为 XML 或 Json。Json.NET 库确实支持将 ISerializable 对象序列化为 json,但该实现存在一个非常小的问题,即类的可序列化构造函数应该是公共的,以便 Json.net 检测到它(请参阅此问题)这确实使 Json.net 无法用于我的情况。

有没有其他方法可以将 ISerializable 对象序列化/反序列化到 xml、Json 或任何其他平面文本格式?

4

2 回答 2

1

Json.NET 实际上确实支持ISerializable类型的非公共流序列化构造函数。如需确认,请参阅 的源代码DefaultContractResolver.CreateISerializableContract()

您的实际问题是有问题的ISerializable类型也是一个collection,并且似乎 Json.NET 使用数组协定而不是JsonISerializableContract此类类型的 a ,如下所示 DefaultContractResolver.CreateContract()

        if (typeof(IEnumerable).IsAssignableFrom(t))
        {
            return CreateArrayContract(objectType);
        }

        if (CanConvertToString(t))
        {
            return CreateStringContract(objectType);
        }

#if !(DOTNET || PORTABLE40 || PORTABLE)
        if (!IgnoreSerializableInterface && typeof(ISerializable).IsAssignableFrom(t))
        {
            return CreateISerializableContract(objectType);
        }
#endif

要解决此问题,您可以创建自己的自定义合约解析器来反转此逻辑:

public class ISerializableCollectionContractResolver : DefaultContractResolver
{
    protected override JsonContract CreateContract(Type objectType)
    {
        var contract = base.CreateContract(objectType);
        var underlyingType = Nullable.GetUnderlyingType(objectType) ?? objectType;

        if (!IgnoreSerializableInterface 
            && typeof(ISerializable).IsAssignableFrom(underlyingType)
            && contract is JsonArrayContract
            && !underlyingType.GetCustomAttributes<JsonContainerAttribute>().Any())
        {
            contract = CreateISerializableContract(objectType);
        }

        return contract;
    }
}

您的自定义集合现在应该通过它们的ISerializable接口进行序列化。

您可能希望缓存合同解析器以获得最佳性能

于 2016-07-04T22:35:44.263 回答
0

DataContractSerializer并且DataContractJsonSerializer都支持ISerializable。请参阅数据协定序列化程序支持的类型

例如,考虑以下类:

[Serializable]
public class SerializableClass : ISerializable
{
    readonly int valueField;

    public SerializableClass(int valueField)
    {
        this.valueField = valueField;
    }

    public int Value { get { return valueField; } }

    #region ISerializable Members

    protected SerializableClass(SerializationInfo info, StreamingContext context)
    {
        this.valueField = info.GetInt32("valueField");
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("valueField", valueField);
    }

    #endregion
}

以及以下辅助方法:

public static partial class DataContractSerializerHelper
{
    public static string SerializeXml<T>(T obj, DataContractSerializer serializer = null, XmlWriterSettings settings = null)
    {
        serializer = serializer ?? new DataContractSerializer(obj.GetType());
        using (var textWriter = new StringWriter())
        {
            settings = settings ?? new XmlWriterSettings { Indent = true, IndentChars = "    " };
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
            {
                serializer.WriteObject(xmlWriter, obj);
            }
            return textWriter.ToString();
        }
    }

    public static T DeserializeXml<T>(string xml, DataContractSerializer serializer = null)
    {
        using (var textReader = new StringReader(xml ?? ""))
        using (var xmlReader = XmlReader.Create(textReader))
        {
            return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(xmlReader);
        }
    }
}

public static partial class DataContractJsonSerializerHelper
{
    private static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
    }

    public static string SerializeJson<T>(T obj, DataContractJsonSerializer serializer = null)
    {
        serializer = serializer ?? new DataContractJsonSerializer(obj.GetType());
        using (var memory = new MemoryStream())
        {
            serializer.WriteObject(memory, obj);
            memory.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(memory))
            {
                return reader.ReadToEnd();
            }
        }
    }

    public static T DeserializeJson<T>(string json, DataContractJsonSerializer serializer = null)
    {
        serializer = serializer ?? new DataContractJsonSerializer(typeof(T));
        using (var stream = GenerateStreamFromString(json))
        {
            var obj = serializer.ReadObject(stream);
            return (T)obj;
        }
    }
}

然后

var test = new SerializableClass(42);

var xml = DataContractSerializerHelper.SerializeXml(test);
Debug.WriteLine(xml);

生产

<SerializableClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.datacontract.org/2004/07/Question38188639">
    <valueField i:type="x:int" xmlns="">42</valueField>
</SerializableClass>

var json = DataContractJsonSerializerHelper.SerializeJson(test);
Debug.WriteLine(json);

生产

{"valueField":42}
于 2016-07-04T20:56:16.770 回答