views:

524

answers:

1

I have a struct in C# that wraps a guid. I'm using DataContractJsonSerializer to serialize an object containing an instance of that class. When I was using a guid directly, it was serialized as a plain string, but now it's serialized as a name/value pair. Here's an NUnit test and supporting code that demonstrates the problem:

 private static string ToJson<T>(T data)
 {
  DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof (T));

  using (MemoryStream ms = new MemoryStream())
  {
   serializer.WriteObject(ms, data);
   return Encoding.Default.GetString(ms.ToArray());
  }
 }

 [Serializable]
 private class ID
 {
  private Guid _value;

  public static explicit operator ID(Guid id)
  {
   return new ID { _value = id };
  }

  public static explicit operator Guid(ID id)
  {
   return id._value;
  }
 }

 [Test]
 public void IDShouldSerializeLikeGuid()
 {
  Guid guid = Guid.NewGuid();
  ID id = (ID) guid;
  Assert.That(ToJson(id), Is.EqualTo(ToJson(guid)));
 }

And the test runner output:

NUnit.Framework.AssertionException:   Expected string length 38 but was 49. Strings differ at index 0.
  Expected: ""7511fb9f-3515-4e95-9a04-06580753527d""
  But was:  "{"_value":"7511fb9f-3515-4e95-9a04-06580753527d"}"
  -----------^

How do I serialize my struct as a plain string and make my test pass?

A: 

In this case it looks like you don't really want JSON, you want a string representation. In that case I would create an interface like this:

interface IStringSerialized
{
    String GetString();
}

Implement this interface on your ID type (and all other types that have similar requirements).

[Serializable]
class ID : IStringSerialized
{
    private Guid _value;

    public static explicit operator ID(Guid id)
    {
     return new ID { _value = id };
    }

    public static explicit operator Guid(ID id)
    {
     return id._value;
    }

    public string GetString()
    {
     return this._value.ToString();
    }
}

Then modify your serialization method to handle these special cases:

private static string ToJson<T>(T data)
{
 IStringSerialized s = data as IStringSerialized;

 if (s != null)
  return s.GetString();

 DataContractJsonSerializer serializer 
                = new DataContractJsonSerializer(typeof(T));

 using (MemoryStream ms = new MemoryStream())
 {
  serializer.WriteObject(ms, data);
  return Encoding.Default.GetString(ms.ToArray());
 }
}
Andrew Hare
I omitted some complexity to make the problem clear, but in the real application this is in the context of a containing object that has a List<ID> in it. I want the real code to produce ["guid1", "guid2"], not [{"_value": "guid1"}, {"_value": "guid2"}]. So unfortunately, that approach won't work for me. Thanks for the idea though!
Brian Victor