到目前为止,这是我设法提出的,我对此非常满意 :-) 只需添加 readResolve/writeReplace,我就完成了!(我可能也会将 Object、SerializationInfo、StreamingContext 参数包装在 ObjectOutputStream 中,以便更好地衡量)。
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using java.io; //My implementation of various Java classes
namespace NewSerializationTest {
public sealed class JavaSerializableSurrogateSelector : ISurrogateSelector
{
public void ChainSelector(ISurrogateSelector selector) { throw new NotImplementedException(); }
public ISurrogateSelector GetNextSelector() { throw new NotImplementedException(); }
public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
{
if (typeof(Serializable).IsAssignableFrom(type))
{
selector = this;
return new JavaSerializationSurrogate();
}
//Type is not marked (java.io.)Serializable
selector = null;
return null;
}
}
public sealed class JavaSerializationSurrogate : ISerializationSurrogate {
//Method called to serialize a java.io.Serializable object
public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) {
//Do the entire tree looking for the 'marker' methods
var type = obj.GetType();
while (type != null)
{
var writeObject = type.GetMethod("writeObject", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext), typeof(Type) }, null );
if (writeObject != null) {
//Class has declared custom serialization so call through to that
writeObject.Invoke(obj, new object[] { info, context, type });
} else {
//Default serialization of all non-transient fields at this level only (not the entire tree)
obj.SerializeFields(info, context, type);
}
type = type.BaseType;
}
}
//Method called to deserialize a java.io.Serializable object
public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) {
//Do the entire tree looking for the 'marker' methods
var type = obj.GetType();
while (type != null)
{
var readObject = type.GetMethod("readObject", BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext), typeof(Type) }, null );
if (readObject != null) {
//Class has declared custom serialization so call through to that
readObject.Invoke(obj, new object[] { info, context, type });
} else {
//Default serialization of all non-transient fields at this level only (not the entire tree)
obj.DeserializeFields(info, context, type);
}
type = type.BaseType;
}
return null;
}
}
[Serializable]
class A : java.io.Serializable {
public string Field1;
}
[Serializable]
class B : A {
public string Field2;
private void readObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
stream.defaultReadObject(context, this, declaringType);
Debug.WriteLine("B: readObject");
}
private void writeObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
stream.defaultWriteObject(context, this, declaringType);
Debug.WriteLine("B: writeObject");
}
}
[Serializable]
class C: B {
public string Field3;
private void writeObject(SerializationInfo stream, StreamingContext context, Type declaringType) {
stream.defaultWriteObject(context, this, declaringType);
Debug.WriteLine("C: writeObject");
}
}
public static class SerializationInfoExtensions {
public static void defaultWriteObject(this SerializationInfo info, StreamingContext context, object o, Type declaringType) {
o.SerializeFields(info, context, declaringType);
}
public static void defaultReadObject(this SerializationInfo info, StreamingContext context, object o, Type declaringType) {
o.DeserializeFields(info, context, declaringType);
}
}
class Program {
static void Main(string[] args) {
var myC = new C { Field1 = "tom", Field2 = "dick", Field3 = "harry" };
using (var ms = new MemoryStream()) {
var binaryFormatter = new BinaryFormatter();
binaryFormatter.SurrogateSelector = new JavaSerializableSurrogateSelector();
binaryFormatter.Serialize(ms, myC);
ms.Position = 0;
var myCDeserialized = binaryFormatter.Deserialize(ms);
}
}
}
/// <summary>
/// Extensions to the object class.
/// </summary>
public static class ObjectExtensions
{
/// <summary>
/// Serializes an object's class fields.
/// </summary>
/// <param name="source">The source object to serialize.</param>
/// <param name="info">SerializationInfo.</param>
/// <param name="context">StreamingContext.</param>
/// <param name="declaringType">The level in the inheritance whose fields are to be serialized - pass null to serialize the entire tree.</param>
public static void SerializeFields(this object source, SerializationInfo info, StreamingContext context, Type declaringType)
{
//Serialize the entire inheritance tree if there is no declaringType passed.
var serializeTree = declaringType == null;
//Set the level in the class heirarchy we are interested in - if there is no declaring type use the source type (and the entire tree will be done).
var targetType = declaringType ?? source.GetType();
//Get the set of serializable members for the target type
var memberInfos = FormatterServices.GetSerializableMembers(targetType, context);
// Serialize the base class's fields to the info object
foreach (var mi in memberInfos)
{
if (serializeTree || mi.DeclaringType == targetType) {
//Specify the name to use as the key - if the entire tree is being done then the names will already have a prefix. Otherwise, we need to
//append the name of the declaring type.
var name = serializeTree ? mi.Name : mi.DeclaringType.Name + "$" + mi.Name;
info.AddValue(name, ((FieldInfo)mi).GetValue(source));
}
}
}
/// <summary>
/// Deserializes an object's fields.
/// </summary>
/// <param name="source">The source object to serialize.</param>
/// <param name="info">SerializationInfo.</param>
/// <param name="context">StreamingContext.</param>
/// <param name="declaringType">The level in the inheritance whose fields are to be deserialized - pass null to deserialize the entire tree.</param>
public static void DeserializeFields(this object source, SerializationInfo info, StreamingContext context, Type declaringType)
{
//Deserialize the entire inheritance tree if there is no declaringType passed.
var deserializeTree = declaringType == null;
//Set the level in the class heirarchy we are interested in - if there is no declaring type use the source type (and the entire tree will be done).
var targetType = declaringType ?? source.GetType();
var memberInfos = FormatterServices.GetSerializableMembers(targetType, context);
// Deserialize the base class's fields from the info object
foreach (var mi in memberInfos)
{
//Only serialize the fields at the specific level requested.
if (deserializeTree || mi.DeclaringType == declaringType)
{
// To ease coding, treat the member as a FieldInfo object
var fi = (FieldInfo) mi;
//Specify the name to use as the key - if the entire tree is being done then the names will already have a prefix. Otherwise, we need to
//append the name of the declaring type.
var name = deserializeTree ? mi.Name : mi.DeclaringType.Name + "$" + mi.Name;
// Set the field to the deserialized value
fi.SetValue(source, info.GetValue(name, fi.FieldType));
}
}
}
}
}