0

NetDataContractSerializer を使用してシリアル化されるフィールドの名前付けを制御するために、一般的なシリアル化サロゲートを使用しようとしています。GetObjectData メソッド内のコレクションへの参照を保持するフィールドに遭遇するたびに問題が発生することを除いて、概念はこれまでのところうまく機能しており、AddValue を呼び出してそれをフィードすると、コレクション型がアイテムにならないようです。連載中のコレクション。配列やリストなどのコレクションに対して何か特別なことをする必要がありますか?

誰かが私を正しい方向に向けることができることを期待して、以下の例を含む完全なコードのコピーを追加しました。

static void Main()
{
    var obj = new Club(
        "Fight Club", 
        Enumerable.Range(1, 3).Select(i => new Member() { Age = i, Name = i.ToString() }));

    var streamingContext = new StreamingContext(StreamingContextStates.Clone);
    //var serializer = new NetDataContractSerializer(streamingContext);
    var serializer = new NetDataContractSerializer(streamingContext, int.MaxValue, false, System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full, new MySurrogateSelector(streamingContext));
    var ms = new MemoryStream();
    serializer.Serialize(ms, obj);

    Console.WriteLine("Before serializing: \n{0}\n", obj);

    ms.Position = 0;
    var xml = XElement.Load(ms);
    Console.WriteLine("Serialized object: \n\n{0}\n", xml);

    ms.Position = 0;
    var deserializedObj = serializer.Deserialize(ms);

    Console.WriteLine("After deserializing: \n{0}\n", deserializedObj);
    Console.ReadKey();
}

public class MySurrogateSelector : ISurrogateSelector
{
    private ISerializationSurrogate _surrogate = new BackingFieldSerializationSurrogate();

    public MySurrogateSelector(StreamingContext streamingContext)
    {
    }

    public void ChainSelector(ISurrogateSelector selector)
    {
        throw new System.NotImplementedException();
    }

    public ISurrogateSelector GetNextSelector()
    {
        throw new System.NotImplementedException();
    }

    public ISerializationSurrogate GetSurrogate(System.Type type, StreamingContext context, out ISurrogateSelector selector)
    {
        selector = null;
        return _surrogate;
    }
}


public class BackingFieldSerializationSurrogate : ISerializationSurrogate
{
    private static Regex _backingFieldRegex = new Regex("<(.*)>k__BackingField", RegexOptions.Singleline | RegexOptions.Compiled);

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
    {
        info.SetType(obj.GetType());
        var fields = obj.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        foreach (var field in fields)
        {
            string propertyName;
            info.AddValue(
                TryGetPropertyNameFromBackingField(field.Name, out propertyName) ? GenerateCustomBackingFieldName(propertyName) :
                field.Name,
                field.GetValue(obj),
                field.FieldType);
        }
    }

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
    {
        var fields = obj.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        foreach (var field in fields)
        {
            string propertyName;
            field.SetValue(
                obj,
                info.GetValue(
                TryGetPropertyNameFromBackingField(field.Name, out propertyName) ? GenerateCustomBackingFieldName(propertyName) :
                field.Name,
                field.FieldType));
        }

        return obj;
    }

    private static bool TryGetPropertyNameFromBackingField(string fieldName, out string propertyName)
    {
        var match = _backingFieldRegex.Match(fieldName);
        if (match.Groups.Count == 1)
        {
            propertyName = null;
            return false;
        }

        propertyName = match.Groups[1].Value;
        return true;
    }

    private static string GenerateCustomBackingFieldName(string propertyName)
    {
        return "_" + propertyName + "_k__BackingField";
    }
}

[Serializable]
public class Member
{
    public int Age { get; set; }

    public string Name { get; set; }

    public override string ToString()
    {
        return string.Format("Member {0}, Age: {1}", Name, Age);
    }
}

[Serializable]
public class Club
{
    public string Name { get; set; }

    public IList<Member> Members { get; private set; }

    public Club(string name, IEnumerable<Member> members)
    {
        Name = name;
        //Members = new List<Member>(members);
        Members = members.ToArray();
    }

    public override string ToString()
    {
        if (Members == null)
        {
            return Name;
        }

        return Name + ", Total members: " + Members.Count + "\n\t" +  string.Join("\n\t", Members);
    }
}

私がこの手法を試している理由は、パフォーマンスへの影響を懸念する内部処理 XmlException を引き起こす問題を回避するためです。

System.Xml.XmlException occurred
  HResult=-2146232000
  Message=Name cannot begin with the '<' character, hexadecimal value 0x3C.
  Source=System.Xml
  LineNumber=0
  LinePosition=1
  StackTrace:
       at System.Xml.XmlConvert.VerifyNCName(String name, ExceptionType exceptionType)
  InnerException: 
4

1 に答える 1

0

申し訳ありませんが、私は他の誰にも返信する機会をあまり与えませんでしたが、根本的な問題と思われるものを見つけました. 基本の SurrogateSelector クラスを調べた後、コレクションに対して適切な種類のサロゲートを返していない可能性があることに気付きました。そのため、最終的に配列を検出して GetSurrogate から null を返そうとするまで、いくつかの道をたどりました。そして魔法のように、すべてが期待どおりに機能し始めたようです。

public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
{
    if (type.IsArray)
    {
        selector = null;
        return null;
    }

    selector = null;
    return _surrogate;
}

最初は、私が問題を抱えていた List のような他のタイプのジェネリック コレクションもこれで修正されたように見えたので少し驚きましたが、本質的には、これらのコレクションはまだ配列と直接オブジェクト参照に分割されているように見えるので、おそらく配列だけが唯一の方法です私が行方不明だった特別なケース。これが、シリアライゼーション サロゲート クラスを使用することの複雑さに対処する同様の問題に遭遇する可能性のある他の誰かに役立つことを願っています。

于 2013-10-03T20:44:27.940 に答える