Skip to content
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 classes with duplicate names #30

Open
RonSijm opened this issue Oct 26, 2022 · 6 comments
Open

Support for classes with duplicate names #30

RonSijm opened this issue Oct 26, 2022 · 6 comments

Comments

@RonSijm
Copy link

RonSijm commented Oct 26, 2022

Hi there, I've used your library, and it seems to work in most of the cases, unless there are classes with duplicate names. I've created two test scenarios:

Scenario 1: Duplicate classes in a different namespace

using Newtonsoft.Json;
using NUnit.Framework;

namespace JsonKnownTypes.UnitTests
{
    public class DuplicateClassInnerClass
    {
        [Test]
        public void BaseClassTest()
        {
            var c = new ClassInContainer();
            var json = JsonConvert.SerializeObject(c);
        }
    }

    [JsonConverter(typeof(JsonKnownTypesConverter<ClassWithClassBase>))]
    public class ClassWithClassBase
    {
        public string Name { get; set; }

    }


    public class ClassInContainer : ClassWithClassBase
    {
    }
}

namespace JsonKnownTypes.UnitTests.a
{

    public class ClassInContainer : ClassWithClassBase
    {
    }
}

namespace JsonKnownTypes.UnitTests.b
{
    public class ClassInContainer : ClassWithClassBase
    {
    }
}

Scenario 2: Nested classes:

using Newtonsoft.Json;
using NUnit.Framework;

namespace JsonKnownTypes.UnitTests
{
    public class DuplicateClassInnerClass
    {
        [Test]
        public void BaseClassTest()
        {
            var c = new ClassContainer1.ClassInContainer();
            var json = JsonConvert.SerializeObject(c);
        }
    }

    [JsonConverter(typeof(JsonKnownTypesConverter<ClassWithClassBase>))]
    public class ClassWithClassBase
    {
        public string Name { get; set; }

    }

    public class ClassContainer1
    {
        // Name: ClassInContainer
        // Full name: JsonKnownTypes.UnitTests.ClassContainer1+ClassInContainer
        public class ClassInContainer : ClassWithClassBase
        {
        }
    }

    public class ClassContainer2
    {
        // Name: ClassInContainer
        // Full name: JsonKnownTypes.UnitTests.ClassContainer2+ClassInContainer
        public class ClassInContainer : ClassWithClassBase
        {
        }
    }
}

Both cases fail in DiscriminatorValuesManager.AddAutoDiscriminators with the exception: JsonKnownTypes.Exceptions.JsonKnownTypesException: 'ClassInContainer discriminator already in use'

I suppose one solution would be to add a JsonDiscriminatorAttribute on all the classes, But in case of the second scenario, it is a common pattern in MediatR that every inner class is either called Query or Command - so I'd have to add an attribute to every single class.

A simple solution seems to be to simply have longer type descriptors:

internal static void AddAutoDiscriminators(this DiscriminatorValues discriminatorValues, Type[] inherited)
{
    foreach (var type in inherited)
    {
        if (discriminatorValues.Contains(type))
            continue;

        discriminatorValues.AddType(type, type.FullName); // Full name instead of just name
    }
}

though of course this is a big breaking change for everyone...

I was wondering if there's a better approach to this - besides downloading this package from source and modifying the type descriptor code

@dmitry-bym
Copy link
Owner

Perhaps it makes sense to add the ability to define a custom resolver like Func<Type, string>

@dmitry-bym
Copy link
Owner

like this

internal static void AddAutoDiscriminators(this DiscriminatorValues discriminatorValues, Type[] inherited)
{
    foreach (var type in inherited)
    {
        if (discriminatorValues.Contains(type))
            continue;

        discriminatorValues.AddType(type, resolver(type));    }
}

resolver = x => x.FullName;

@dmitry-bym
Copy link
Owner

@RonSijm Then the general behavior will not change, but you can redefine what you need

@dmitry-bym
Copy link
Owner

Do you want to do that?

@RonSijm
Copy link
Author

RonSijm commented Nov 21, 2022

Sure, a Func<Type, string> sounds good.

I have a working prototype locally, but I'm not sure about the implementation. I tried multiple different things, like I've added a public Func<Type, string> NameDiscriminatorResolver { get; set; } = type => type.Name; in JsonKnownTypes.JsonDiscriminatorSettings - which works.

The problem with that was that it changes the settings project-wide. Ideally it should be possible to set this name resolver in an attribute or something - but it doesn't seem very possible to do so.

I have an implementation that works with multiple different types - I'll try to create a PR for it

@RonSijm
Copy link
Author

RonSijm commented Nov 22, 2022

I've created a PR with the implementation I was using: #31

  • It implements the Func<Type, string> NameDiscriminatorResolver
  • It also implements a type specific JsonKnownTypesSettingsManager<T> - because I didn't want to change the behavior for all types, but only for specific types.

I've added a test to show it works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants