Giter Club home page Giter Club logo

Comments (10)

manuc66 avatar manuc66 commented on May 18, 2024

Hi @sanzor

It's not supported, but if you have both:

  • a field in Child1 that is not in Child2
  • a field in Child2 that is not in Child1

You can use JsonSubtypes.KnownSubTypeWithProperty :

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 }
}

from jsonsubtypes.

sanzor avatar sanzor commented on May 18, 2024

Thank you very much for your answer !

from jsonsubtypes.

Peter-B- avatar Peter-B- commented on May 18, 2024

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.

from jsonsubtypes.

manuc66 avatar manuc66 commented on May 18, 2024

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

from jsonsubtypes.

Peter-B- avatar Peter-B- commented on May 18, 2024

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?

from jsonsubtypes.

manuc66 avatar manuc66 commented on May 18, 2024

Hi @Peter-B- ,

I think that this solution is implementable but will have limitation: no possibility to handle multiple discriminated property because only one JsonWithDiscriminatedSubtypeProperty (JsonConverter) will be instantiated.

[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;}
}

=> 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 JsonSubtypes.cs and :

  • re-use the trick behind _isInsideRead logic (see discussion inside this PR #39 )
  • re-use the array/collection reading (to support a public List<Child> Payload {get; set;})
  • re-use type find algorithm
  • still provide(*) the possibility to support runtime configuration (similarly to JsonSubtypesConverterBuilder)
  • serializing and provide(*) the possibility to auto inject the discriminator in the json
  • ...

(*): or consider that it will possible to later add

from jsonsubtypes.

manuc66 avatar manuc66 commented on May 18, 2024

@Peter-B-

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 
{

}

from jsonsubtypes.

Peter-B- avatar Peter-B- commented on May 18, 2024

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?

from jsonsubtypes.

manuc66 avatar manuc66 commented on May 18, 2024

@Peter-B-

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

from jsonsubtypes.

manuc66 avatar manuc66 commented on May 18, 2024

Maybe #91 will help

from jsonsubtypes.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.