Test for Optional Field when using .NET Custom Serialization
Asked Answered
L

3

16

Given a class like this one:

[Serializable]
public class MyClass {
    string name;
    string address;

    public MyClass(SerializationInfo info, StreamingContext context){
        name = info.GetString("name");
        if(/* todo: check if a value for address exists */)
            address = info.GetString("address");
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context){
        info.AddValue(name);
        if(address != null)
            info.AddValue(address);
    }
}

How do I test whether a value for the address field exists before calling info.GetString(address)?

Yes, I do understand that I could simply write a null address field but my real problem is that earlier versions of MyClass, did not have an address field.

Note: I have good reasons for using custom serialization. There are some static fields that are being used as singletons and the default deserialization will not respect that.

Loudermilk answered 26/8, 2009 at 8:33 Comment(0)
T
22

Well, one intriguing approach is that you could use GetEnumerator (foreach) to iterate over the name/value pairs, using a switch on the name to handle each in turn?

The implementation seems a bit non-standard, though; from the example here:

    SerializationInfoEnumerator e = info.GetEnumerator();
    Console.WriteLine("Values in the SerializationInfo:");
    while (e.MoveNext())
    {
        Console.WriteLine("Name={0}, ObjectType={1}, Value={2}",
             e.Name, e.ObjectType, e.Value);
    }

But it looks like you can also use SerializationEntry:

[Serializable]
class MyData : ISerializable
{
    public string Name { get; set; }
    public int Value { get; set; }

    public MyData() { }
    public MyData(SerializationInfo info, StreamingContext context)
    {
        foreach (SerializationEntry entry in info)
        {
            switch (entry.Name)
            {
                case "Name":
                    Name = (string)entry.Value; break;
                case "Value":
                    Value = (int)entry.Value; break;
            }
        }
    }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Name", Name);
        info.AddValue("Value", Value);
    }
}
Tomchay answered 26/8, 2009 at 8:45 Comment(0)
M
1

I know this is a very old thread, but my solution to this issue was to create an extension method of the SerializationInfo class like this:

namespace System.Runtime.Serialization
{
  public static class SerializationInfoExtensions
  {
    public static bool Exists(this SerializationInfo info, string name)
    {
      foreach (SerializationEntry entry in info)
        if (name == entry.Name)
          return true;
      return false;
    }
  }
}
Mohican answered 27/2, 2019 at 14:45 Comment(0)
M
0

@Lee Griffin answer is inefficient if you need to check multiple members as it will iterate all members for each property.

I found creating a extension method that will create a dictionary from the members more efficient. If you just need to test for existence, you can create a hashset to take advantage of faster lookups then a basic list.

// if you don't care about the object type then you can replace the tuple with just object
public static Dictionary<string, Tuple<Type, object>> GetSerializedMemberDictionary(this SerializationInfo information)
{
    var memberDict = new Dictionary<string, Tuple<Type, object>>(information.MemberCount);
    foreach (SerializationEntry entry in information)
       memberDict.Add(entry.Name, Tuple.Create(entry.ObjectType, entry.Value));
    return memberDict;
}

public static HashSet<string> GetSerializedMemberList(this SerializationInfo information)
{
    var list = new HashSet<string>(information.MemberCount);
    foreach (SerializationEntry entry in information)
       list.Add(entry.Name);
    return list;
}

it can then be used simply:

var members = information.GetSerializedMemberDictionary();
if (members.ContainsKey("Delimiter")) {
   _delimiter = (string)members["Delimiter"].Item2;
}
        

or

var members = information.GetSerializedMemberList();
var delimiter = members.Contains("Delimiter") ? information.GetString("Delimiter") : null;

        
Mady answered 19/1, 2023 at 19:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.