-
-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Discriminator outside Discriminated object #61
Comments
Hi @sanzor It's not supported, but if you have both:
You can use public class Parent{
Child childField {get;set;}
}
[JsonConverter(typeof(JsonSubtypes))]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Child1), "FieldChild1")]
[JsonSubtypes.KnownSubTypeWithProperty(typeof(Child2), "FieldChild2")]
public abstract class Child {
}
public class Child1 : Child{
public int FieldChild1 {get;set;}
}
public class Child2 : Child{
public bool FieldChild2 {get;set;}
} And a JSON without discriminator field: {
"childField": { "FieldChild2": true }
} |
Thank you very much for your answer ! |
I am facing the same issue. Do you think this might be possible to achieve with this library? If so, do you have any hint where I might get started? Maybe I try to implement that. |
Note that there is also this UGLY solution if you're really blocked: public enum Discriminator{
Child1=0,
Child2=1
}
[JsonConverter(typeof(JsonSubTypes), "Kind")]
[JsonSubTypes.KnownSubTypes(typeof(ParentWithChild1), Discriminator.Child1)]
[JsonSubTypes.KnownSubTypes(typeof(ParentWithChild2), Discriminator.Child2)]
public class Parent {
public abstract Discriminator Kind {get;}
}
public class ParentWithChild1 {
public Child1 childField {get ;set;}
public override Kind=>Discriminator.Child1;
}
public class ParentWithChild2 {
public Child2 childField {get; set;}
public override Kind=>Discriminator.Child2;
}
public abstract class Child {
}
public class Child1 : Child{
public int FieldChild1 {get; set;}
}
public class Child2 : Child{
public bool FieldChild2 {get; set;}
} By the way a proper implementation should first find a way to declare the relation elegantly, a proposal could be : [JsonConverter(typeof(JsonWithDiscriminatedProperty))]
[JsonResolvePropertySubtypes("childField",
new object[] { Discriminator.Child1, Discriminator.Child2 },
new[] { typeof(Child1), typeof(Child2) })]
public class Parent
{
Child childField { get; set; }
} especially the fact that there is no way to ensure at compile time that the array are the same dimensions and It's not either easy to read. (Not that it could allow to multiple discriminated fields) I've also thought to this: [JsonConverter(typeof(JsonWithDiscriminatedSubtypeProperty), "kindFieldA")]
[JsonResolvePropertyValueWithSubtypes(Discriminator.Child1, typeof(Child1))]
[JsonResolvePropertyValueWithSubtypes(Discriminator.Child2, typeof(Child1))]
public class Parent
{
public Discriminator kindFieldA { get; }
Child childField { get; set; }
} but there is nothing that make the link between the kind property and the discriminated child... (in case of multiple discriminated property with the same base type) I'm open to propositions |
Thanks for your prompt replay and the effort you put into this. To tell the truth, I didn't think a whole lot about how to configure it. And I also have to admit, that I am not blocked by this right now. I am guaranteed to have no nesting of those types, so I can just use bare Json.Net to convert the object and then continue to parse the returned JObject into the correct child type. But since others might have the same problem, I will try to give more details. Maybe this can help to decide for an approach to annotate this. My Json looks like this: {
"ContentType": "TypeA",
"Payload": {
"Name": "Joe"
}
} and {
"ContentType": "TypeB",
"Payload": {
"Number": 42
}
} The parent C# class might look like public class Parent
{
public string ContentType {get; set;} // Might be an enum
public Child Payload {get; set;}
} So I could imagine an annotation similar to your last example: [JsonConverter(typeof(JsonWithDiscriminatedSubtypeProperty), "ContentType")]
[JsonResolvePropertyValueWithSubtypes("TypeA", typeof(ChildTypeA))]
[JsonResolvePropertyValueWithSubtypes("TypeB", typeof(ChildTypeB))]
public class Parent
{
public string ContentType {get; set;} // Might be an enum
public Child Payload {get; set;}
} Do you think this can be implemented? |
Hi @Peter-B- , I think that this solution is implementable but will have limitation: no possibility to handle multiple discriminated property because only one
=> I would prefer a solution that does not come with this limitation The implementation of this feature is technically feasible but the complexity is not 'trivial', from my point of view it should refactor
(*): or consider that it will possible to later add |
What about something like this: [JsonConverter(typeof(JsonPropertySubTypes))]
public class Parent
{
public string ContentType1 {get; set;} // Might be an enum
[JsonSubTypes.ResolveWithProperty(nameof(ContentType1))
public Child Payload1 {get; set;}
public string ContentType2 {get; set;} // Might be an enum
[JsonSubTypes.ResolveWithProperty(nameof(ContentType2))
public Child Payload2 {get; set;}
}
[JsonSubTypes.KnownSubTypes(typeof(ChildTypeA), "TypeA")]
[JsonSubTypes.KnownSubTypes(typeof(ChildTypeB), "TypeB")]
public class Child
{
} |
That is exactly my scenario. I also think the syntax is quite readable - at least way better than the workaround from above. Do you think this is easy to implement or do you anticipate large architectural changes? |
It does not depend on a lot of existing code, if you implement it with another custom converter it could be done this way: public class SomeClasswWithTwoAbstract : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
JsonSerializer serializer)
{
var parent = new Parent();
var json = JObject.Load(reader);
var parentType = typeof(Child);
var parentTypeFullName = parentType.FullName;
var searchLocation = parentTypeFullName.Substring(0, parentTypeFullName.Length - parentType.Name.Length);
var typeName1 = json["ContentType1"].Value<string>();
var typeName2 = json["ContentType2"].Value<string>();
var contentType1 = parentType.Assembly.GetType(searchLocation + typeName1, false, true);
var contentType2 = parentType.Assembly.GetType(searchLocation + typeName2, false, true);
parent.Payload1 = (Child) serializer.Deserialize(CreateAnotherReader(json["Payload1"], reader), contentType1);
parent.Payload2 = (Child) serializer.Deserialize(CreateAnotherReader(json["Payload2"], reader), contentType2);
return parent;
}
private static JsonReader CreateAnotherReader(JToken jToken, JsonReader reader)
{
// duplicate code
var jObjectReader = jToken.CreateReader();
jObjectReader.Culture = reader.Culture;
jObjectReader.CloseInput = reader.CloseInput;
jObjectReader.SupportMultipleContent = reader.SupportMultipleContent;
jObjectReader.DateTimeZoneHandling = reader.DateTimeZoneHandling;
jObjectReader.FloatParseHandling = reader.FloatParseHandling;
jObjectReader.DateFormatString = reader.DateFormatString;
jObjectReader.DateParseHandling = reader.DateParseHandling;
return jObjectReader;
}
} See the working sample: https://dotnetfiddle.net/nv1EpP |
Maybe #91 will help |
Hello i was wondering if you can manage to to deserialize a json that has its
Discriminator
outside the Discriminated object:The below example works
What i want to achieve :
Could you move the
Kind
field to the parent object and decorate the parent so that it uses theKind
field to discriminate thechildField
?Detailed question here
Long story short
Can i accomodate from this json:
To this json:
The text was updated successfully, but these errors were encountered: