简体   繁体   中英

How to serialize Manatee.Json.JsonValue?

I use Manatee.Json.JsonValue a whole lot, for instance in a case I have now, where I have a property of type object . This object property can contain both literals or complex types.

When the property value contains a complex type of JsonValue , it is serialized as an empty object! Is it possible to avoid this, such that other property values are serialized, while object property values of type JsonValue are just copied?

Here is an example with Manatee.Json.13.0.2 :

public class TestObject
{
    public object Property { get; set; }
}

[TestClass]
public class TestClass
{
    [TestMethod]
    public void Test()
    {
        var testObject = new TestObject
        {
            Property = new JsonValue("testString")
        };

        JsonValue expected = new JsonValue(
            new JsonObject
            {
                ["Property"] = "testString",
            }
        );

        var serializer = new Manatee.Json.Serialization.JsonSerializer();
        JsonValue actual = serializer.Serialize(testObject);

        var expectedString = expected.ToString();
        /// expectedString  "{\"Property\":\"testString\"}" string

        var actualString = actual.ToString();
        /// actualString    "{\"Property\":{}}" string

        Assert.AreNotEqual(expectedString, actualString);
    }
}

Anyone have any idea how to do this?

So, I've come to find a solution that seems to cover my use case. I have managed to create a simple JsonSerializer that copies values. Upon serialization, the goal is to go from the source object to a JsonValue target. If the source value is either a JsonValue , a JsonObject or a JsonArray instance, the serialization method returns a JsonValue . Other types throws an ArgumentOutOfRangeException .

Upon deserialization, the goal is to go from a JsonValue and back to the requested type. If the requested type is a JsonValue , the current JsonValue are returned. If the requested type is a JsonObject , either the current JsonValue.Object are returned, or a new JsonObject if JsonValue.Object is null. Same goes for JsonArray : JsonValue.Array ?? new JsonArray . Other requested types returns null;

The serializer must be registered with the SerializerFactory.AddSerializer(mySerializer) somewhere. For me it is typically in Global.asax.cs and in a MSTest [AssemblyInitialize()] method.

Here is my implementation at this point:

public class JsonValueSerializer : ISerializer
{
    public bool ShouldMaintainReferences => false;

    private Type[] HandledTypes = {
        typeof(JsonValue),
        typeof(JsonObject),
        typeof(JsonArray)
    };

    public bool Handles(SerializationContextBase context)
    {
        return HandledTypes.Contains(context.InferredType);
    }

    public JsonValue Serialize(SerializationContext context)
    {
        if (context.Source is JsonValue jsonValue)
            return jsonValue;
        else if (context.Source is JsonObject jsonObject)
            return new JsonValue(jsonObject);
        else if (context.Source is JsonArray jsonArray)
            return new JsonValue(jsonArray);
        else
            throw new ArgumentOutOfRangeException();
    }

    public object Deserialize(DeserializationContext context)
    {
        if (context.RequestedType == typeof(JsonValue))
        {
            return context.LocalValue;
        }
        else if (context.RequestedType == typeof(JsonObject))
        {
            return context.LocalValue.Object ?? new JsonObject();
        }
        else if (context.RequestedType == typeof(JsonArray))
        {
            return context.LocalValue.Array ?? new JsonArray();
        }
        return null!;
    }
}

Weakness of this implementation:
Deserialization can be done with serializer.Deserialize<JsonObject>(myJsonValue) . A problem with this implementation, is that when I have a complex class with an object property, and that property contains a JsonObject (or a JsonArray ), it works to serialize the complex type into JsonValue , but upon deserialization, the previous JsonObject would be deserialized into a JsonValue of JsonValueType.Object . This is different from the original JsonObject , since JsonValue can contain a JsonObject , but JsonObject is a type in its own right.

Without additional mechanisms, it would be impossible to know if the JsonValue should be kept or if it should be turned back into a JsonObject or a JsonArray . One could decide that on deserialization, all JsonValue.Object would be constantly set to a JsonObject type, but that would be wrong when it initially was a JsonValue of JsonValueType.Object (same for JsonArray ). Another approach could maybe be to let JsonObject get a "$type" parameter with the information, but that wouldn't work with JsonArray . I have no fix for this behaviour.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM