Reputation: 2607
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?
Upvotes: 1
Views: 188
Reputation: 2607
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.
Upvotes: 1