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

Deserializing Collections / Dictionaries that have Getters only #105

Open
BOBJohnson opened this issue Dec 20, 2020 · 2 comments
Open

Deserializing Collections / Dictionaries that have Getters only #105

BOBJohnson opened this issue Dec 20, 2020 · 2 comments
Labels

Comments

@BOBJohnson
Copy link

BOBJohnson commented Dec 20, 2020

Code analysis gives me grief if a property that is a collection has a setter (see https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2227).

If I remove the setter, YAXLib will not correctly deserialize collection. I also tested with a basic Dictionary as well. Basically, it serializes correctly (the items are there in the Xml output), but when deserializing I just get an empty collection / dictionary.

If I use the .Net XmlSerializer (which is a hunk of junk) it throws an exception on the Dictionary for being a Dictionary, but for the List, it deserializes correctly.

I can work around it by adding a private setter - YAXLib correctly deserializes that.

Some extra info that might be relevant:

I'm currently working in a new .Net 5.0 project so I don't know if it is only relevant to YAXLib.Core I'm referencing via NuGet.

I downloaded the source code to poke around, but I can't open the Core solution. I'm using VS 2019, and apparently you're 2015 solution is not compatible. So instead I was working on the other solution. In the other solution, I couldn't find anything for Collections, but I found this bit poking around for Dictionary:

           else if (ReflectionUtils.IsNonGenericIDictionary(colType))
            {
                object col = containerObj;
                foreach (var lstItem in lst)
                {
                    object key = lstItem.GetType().GetProperty("Key", BindingFlags.Instance | BindingFlags.Public).GetValue(lstItem, null);
                    object value = lstItem.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public).GetValue(lstItem, null);

                    try
                    {
                        colType.InvokeMethod("Add", col, new[] {key, value});
                        //colType.InvokeMember("Add", BindingFlags.InvokeMethod, null, col, new[] { key, value });
                    }
                    catch
                    {
                        OnExceptionOccurred(new YAXCannotAddObjectToCollection(memberAlias.ToString(), lstItem, xelemValue), m_defaultExceptionType);
                    }
                }

                return col;
            }

So if I'm reading that correctly, it shouldn't care that the Dictionary has no setter on the property because it's calling the "Add" method via reflection.


While I do have a simple workaround by adding a private set, there is a couple reasons why I would like your wonderful library to support Collections / Dictionaries without setters:

  1. I'd rather not add a superfluous setter as a workaround, plus I want other developers on my team thinking they can use the private setter - they should be using the Clear method instead.
  2. I haven't tested it in this new project, but I've had some specialized collection / dictionary classes in the past, where it did special logic on the Add method (such as wiring up INotifyPropertyChanged events as an example). So I want to make sure the Add function is called, and not the property setter on the collection itself. It may not break the chain of events, but I'd rather be on the safe side just in case.

One last request related to this one: it'd be nice if you could add a new option to the YAXSerializationOptions, specifically, you have DontSerializePropertiesWithNoSetter, so I'd like an extra option like 'SerializeCollectionPropertiesWithNoSetter', which would overrule the other.

Slight caveat on that new mode: it should only do it on non-read only collections. Specifically, such as IList has an IsReadOnly bool property - if the collection implements IList or IDictionary and has IsReadOnly = true, and it does not have a setter, then it should be treated like a property with no setter. ICollection has no IsReadOnly property.

Course that's just a "it'd be nice to have". I workaround it by not using the DontSerializePropertiesWithNoSetter option, and then for stuff that is read only properties, just add the Don't Serialize attribute to them.

@axunonb
Copy link
Collaborator

axunonb commented Mar 3, 2021

Hi, this has been a while ago, sorry. We're currently about revitalizing this project, and we're going through open issues.

I'm using VS 2019, and apparently you're 2015

Meanwhile the repo is on VS2019

We'd be more than happy about a PR (with tests), where we could see and discuss the concrete implementation you have in mind. What do you think?

@axunonb
Copy link
Collaborator

axunonb commented Aug 17, 2022

To resolve this issue, it looks like 2 things were to be modified:

  1. Remove the general test, whether a member can write. Only these members are used in the deserialization process.

    if (!member.CanWrite)
    return false;

  2. If we can't write to the member, and it is initialized, add the colObject items

    member.SetValue(o, colObject);

Quite some refactoring and testing regarding side-effects is to be expected.

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

No branches or pull requests

3 participants