How can I make DataContractJsonSerializer serialize an object as a string?
Asked Answered
K

2

12

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?

Klarrisa answered 24/7, 2009 at 14:59 Comment(0)
O
10

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());
    }
}
Osuna answered 24/7, 2009 at 15:8 Comment(2)
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!Klarrisa
Don't use Default encoding, as it will corrupt any non-ANSI characters. I'm currently dealing with Mobile phone push messages. Your code should be able to deal with other languages. Use instead, UTF8. return Encoding.UTF8.GetString(ms.ToArray());Diary
T
1

Try using the JavaScriptSerializer Class it will prevent the key, value bloat issue.

Treatment answered 2/2, 2012 at 8:46 Comment(1)
but then you will loss all the data contracts attributes that you have in you object the you would like to serialize (like emitdefaultvalue)Radical

© 2022 - 2024 — McMap. All rights reserved.