问题
Here is my example:
JSON request string:
{
entity: '09f7cb28-0464-41c8-a20d-1b05eb1cda0a'
}
My request object:
public class Request {
public Request() { }
[JsonProperty("entity")]
private string EntityIdentifier { get; set; }
public EntityObject Entity { get; set; }
}
I've got this to work so that the string is passed to the EntityIdentifier, which is fine, then in my code I find the actual entity using the entity identifier property, populate the Entity property with the found entity but then this is what I get when I serialize the object:
{
entity: '09f7cb28-0464-41c8-a20d-1b05eb1cda0a',
Entity: {
// my object's properties
}
}
When all I really want is:
{
entity: {
// my object's properties
}
}
Now, I know I could split this out into two different classes, and I may have to, but if there is a way to keep it all in the same class, it would be awesome and save me a lot of coding time.
To clarify because I seem to not be explaining what I want very well:
Disclaimer: This doesn't exist (AFAIK) but this is what I would like:
public class Request {
public Request() { }
[JsonProperty("entity", OnlyWhen=Deserializing)]
private string EntityIdentifier { get; set; }
[JsonProperty("entity", OnlyWhen=Serializing)]
public EntityObject Entity { get; set; }
}
This is what I would really like to achieve, but as far as I can see the only place I could realistically put this type of code is in a custom converter, but unfortunately, I can't seem to be able to determine whether the converter is being used for serialization or deserialization when it is being used.
回答1:
You can solve this problem with a custom JsonConverter
similar to this:
public class RequestConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Request));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// On deserialization, the JSON has an entity identifier (GUID)
// so use it to retrieve the actual object from the database.
JToken token = JToken.Load(reader);
Request req = new Request();
req.Entity = RetrieveFromDatabase(token["entity"].ToString());
return req;
}
private EntityObject RetrieveFromDatabase(string entityIdentifier)
{
// Implement this method to retrieve the actual EntityObject from the DB.
// (Return null if the object is not found.)
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// On serialization, write out just the entity object properties
Request req = (Request)value;
JObject obj = new JObject();
obj.Add("entity", JToken.FromObject(req.Entity));
obj.WriteTo(writer);
}
}
To use the converter, simply decorate your Request
class with a [JsonConverter]
attribute as shown below. Note that you can remove the private EntityIdentifier
property, as it will no longer be needed. The converter has responsibility for retrieving the EntityObject
from the database based on the identifier in the JSON.
[JsonConverter(typeof(RequestConverter))]
public class Request
{
public Request() { }
public EntityObject Entity { get; set; }
}
Note, if you don't want the converter to have responsibility for populating the entity object, you can still use the converter idea. In that case, make the ReadJson
method set the EntityIdentifier
property on the request. However, since you have made this property private, you will either need to use reflection to do this, make the property public, or make a constructor for the Request that the converter can use to instantiate it with the identifier.
回答2:
How about using the JsonIgnore attribute?
public class Request
{
public Request() { }
[JsonIgnore]
private string EntityIdentifier { get; set; }
[JsonProperty("entity")]
public EntityObject Entity { get; set; }
}
回答3:
I believe you just need to mark Entity with [ScriptIgnore] and/or [JsonIgnore], like this:
[ScriptIgnore]
[JsonIgnore]
public EntityObject Entity { get; set; }
I have heard of JsonIgnore not working sometimes. Using both is probably your best bet.
Also - I believe your wording is incorrect when describing the problem. You state: "this is what I get when I deserialize the object" - when in fact, I believe you mean to say "serialize".
If you need to populate Entity when EntityIdentifier is set, then replace the code for EntityIdentifier with something like:
string _eId;
[JsonProperty("entity")]
private string EntityIdentifier
{
get{return _eId;}
set
{
_eId = value;
Entity = someMethodToRetrieveTheEntityById(_eId);
}
}
回答4:
The problem here could be that you're trying to force one class do the job of two. Why not do:
// Deserialize requests into this.
public class EntityRequest
{
[JsonProperty("entity")]
private string EntityIdentifier { get; set; }
}
// Serialize these to file/etc.
public class EntityData
{
[JsonProperty("entity")]
public EntityObject Entity { get; set; }
}
Then you deserialize requests into EntityRequest objects, load EntityData objects using the EntityRequest and some other logic, then serialize the EntityData objects to file. The JsonProperty attributes here mean that both the request and the output entity are both called 'entity' just like in your OP. This seems to be the workflow you're after.
Cramming everything into one class is making this problem more complicated than it needs to be.
来源:https://stackoverflow.com/questions/25118420/is-it-possible-to-serialize-an-object-differently-to-deserializing-it-using-json