how to filter/transform object attributes to be serialized with jsonfx?

834 Views Asked by At

I use JsonFX to serialize an instance to json, and I need to ignore some attributes and preprocess others. How do I do that ? The doc is not very clear on that.

My class looks a bit like this one :

public class Primitive{
    public string type;
    public float[] vertices;
    public int[] indices;
    public int[] edgeIndices;
    public Scene scene;

When outputting json, I need to ignore the scene attribute (that's the whole 3d scene) and to pre-process the vertices, indices, edgeIndices using some compression scheme.

And, obviously, I need the symmetrical operation when receiving json.

But how ?

1

There are 1 best solutions below

2
On BEST ANSWER

You can use [JsonIgnore] along with JsonResolverStrategy:

public class Scene
{
    public string Name { get; set; } // Or whatever
}

public class Primitive
{
    public string type;
    public float[] vertices;
    public int[] indices;
    public int[] edgeIndices;
    [JsonFx.Json.JsonIgnore]
    public Scene scene;
}

public class TestClass
{
    public static void Test()
    {
        var primitive = new Primitive
        {
            type = "some type",
            vertices = new[] { 1.0f, 2.0f },
            indices = new[] { 1, 2 },
            edgeIndices = new[] { 0, 1 },
            scene = new Scene { Name = "Should Not Be Serialized" }
        };

        var writer = new JsonWriter(new DataWriterSettings(new JsonResolverStrategy()));

        var json = writer.Write(primitive);

        Debug.WriteLine(json);  // Prints {"type":"some type","vertices":[1,2],"indices":[1,2],"edgeIndices":[0,1]}
        Debug.Assert(!json.Contains("Should Not Be Serialized")); // No assert
    }
}

Or, you can use data contract attributes along with DataContractResolverStrategy:

public class Scene
{
    public string Name { get; set; } // Or whatever
}

[DataContract]
public class Primitive
{
    [DataMember]
    public string type;
    [DataMember]
    public float[] vertices;
    [DataMember]
    public int[] indices;
    [DataMember]
    public int[] edgeIndices;
    [IgnoreDataMember]
    public Scene scene;
}

public class TestClass
{
    public static void Test()
    {
        var primitive = new Primitive
        {
            type = "some type",
            vertices = new[] { 1.0f, 2.0f },
            indices = new[] { 1, 2 },
            edgeIndices = new[] { 0, 1 },
            scene = new Scene { Name = "Should Not Be Serialized" }
        };

        var writer = new JsonWriter(new DataWriterSettings(new DataContractResolverStrategy()));

        var json = writer.Write(primitive);

        Debug.WriteLine(json);
        Debug.Assert(!json.Contains("Should Not Be Serialized")); // No assert
    }
}

(Note however that DataContractResolverStrategy is broken in .Net 3.5., so you can't use it if you're stuck on that old version.)

Update

One way to "transform" properties during serialization is to use proxy properties, for instance:

public class Primitive
{
    public string type;

    public float[] vertices;

    [JsonFx.Json.JsonIgnore]
    public int[] indices;

    [JsonFx.Json.JsonName("indices")]
    public string compressedIndices
    {
        get
        {
            return indices.Base64Compress();
        }
        set
        {
            indices = CompressionExtensions.Base64DecompressIntArray(value);
        }
    }

    public int[] edgeIndices;

    [JsonFx.Json.JsonIgnore]
    public Scene scene;
}

public static class CompressionExtensions
{
    public static string Base64Compress(this IEnumerable<int> values)
    {
        if (values == null)
            return null;
        using (var stream = new MemoryStream())
        {
            using (var compressor = new DeflateStream(stream, CompressionMode.Compress, true))
            {
                var _buffer = new byte[4];
                foreach (var value in values)
                {
                    _buffer[0] = (byte)value;
                    _buffer[1] = (byte)(value >> 8);
                    _buffer[2] = (byte)(value >> 16);
                    _buffer[3] = (byte)(value >> 24);
                    compressor.Write(_buffer, 0, 4);
                }
            }
            return Convert.ToBase64String(stream.GetBuffer(), 0, checked((int)stream.Length)); // Throw an exception on overflow.
        }
    }

    public static int[] Base64DecompressIntArray(string base64)
    {
        if (base64 == null)
            return null;
        var list = new List<int>();
        var m_buffer = new byte[4];
        using (var stream = new MemoryStream(Convert.FromBase64String(base64)))
        {
            using (var compressor = new DeflateStream(stream, CompressionMode.Decompress))
            {
                while (compressor.Read(m_buffer, 0, 4) == 4)
                {
                    list.Add((int)(m_buffer[0] | m_buffer[1] << 8 | m_buffer[2] << 16 | m_buffer[3] << 24));
                }
            }
        }
        return list.ToArray();
    }
}

Then write and read like so:

        var primitive = new Primitive
        {
            type = "some type",
            vertices = new[] { 1.0f, 2.0f },
            indices = Enumerable.Range(0, 10000).Select(i => 0).ToArray(),
            edgeIndices = new[] { 0, 1 },
            scene = new Scene { Name = "Should Not Be Serialized" }
        };

        var writer = new JsonWriter(new DataWriterSettings(new JsonResolverStrategy()));

        var json = writer.Write(primitive);

        var reader = new JsonReader(new DataReaderSettings(new JsonResolverStrategy()));
        var primitiveBack = reader.Read<Primitive>(json);

        Debug.Assert(primitiveBack.indices.SequenceEqual(primitive.indices));