castleproject / Core

Castle Core, including Castle DynamicProxy, Logging Services and DictionaryAdapter
http://www.castleproject.org/
Other
2.2k stars 467 forks source link

DictionaryAdapter Nesting Dictionaries JSON like #673

Closed winxalex closed 2 weeks ago

winxalex commented 9 months ago

Case: Response from api in JSON like Dictionaries (<string,object>,<object,object> )and List Expando Object as candidate but not working in .NET Core 2.1 (would to be good benchmarking Expando Vs DictionaryAdapter)

using System;
using System.Collections;
using Castle.Components.DictionaryAdapter;

namespace x
{
    public class DictionaryAdapterAttribute : DictionaryBehaviorAttribute,
        IPropertyDescriptorInitializer, IDictionaryPropertyGetter
    {
        private Type __type;

        public DictionaryAdapterAttribute(Type type)
        {
            __type = type;
        }

        public object GetPropertyValue(IDictionaryAdapter dictionaryAdapter, string key, object storedValue,
            PropertyDescriptor property, bool ifExists)
        {
            if (storedValue != null && storedValue.GetType().IsGenericType &&
                storedValue is IDictionary dictionary)
            {
                var adapter = dictionaryAdapter.Meta.Factory.GetAdapter(__type,
                    dictionary);

                dictionaryAdapter.SetProperty(key, ref adapter);

            }

            return storedValue;
        }

        public void Initialize(PropertyDescriptor propertyDescriptor, object[] behaviors)
        {
            propertyDescriptor.Fetch = true;
        }
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
using Castle.Components.DictionaryAdapter;

namespace X
{
     /// <summary>
        ///  will convert List<Dictionaries> to List<of addapters of Dictionaries>(default) or some other Collection Class
        ///  which should have constructor accepting IEnumerable
        /// </summary>
        public class ListOfDictionariesAdapterAttribute : DictionaryBehaviorAttribute,
            IPropertyDescriptorInitializer, IDictionaryPropertyGetter
        {
            private Type __type;
            private bool __adapted;

            public ListOfDictionariesAdapterAttribute(Type type)
            {
                __type = type;
            }

            public object GetPropertyValue(IDictionaryAdapter dictionaryAdapter, string key, object storedValue,
                PropertyDescriptor property, bool ifExists)
            {
                // var o = dictionaryAdapter.GetProperty(key, ifExists);

                if (!__adapted && storedValue != null && storedValue.GetType().IsGenericType &&
                    storedValue is IEnumerable enumerable)
                {

                    if (enumerable.GetType().GetGenericArguments()[0] != __type)
                    {
                        var enumerator = enumerable.GetEnumerator();
                        var genericArgument = __type.GetGenericArguments()[0];
                        IList ilistWrapper =
                            Activator.CreateInstance(typeof(List<>).MakeGenericType(genericArgument)) as IList;

                        while (enumerator.MoveNext())
                        {
                            if (enumerator.Current is IDictionary current)
                                ilistWrapper?.Add(dictionaryAdapter.Meta.Factory.GetAdapter(genericArgument,
                                    current));
                        }

                        object list;

                        if (__type.GetGenericTypeDefinition()==typeof(List<>))
                        {
                            //instead of List<Dictionaries> swap with List<adapters of Dictionaries>
                            list = ilistWrapper;
                        }
                        else
                        {
                            //!! Note maybe use https://github.com/GriffinPlus/dotnet-libs-fastactivator instead of 
                            //collection must have constructor that accept enumerable as params
                            list = Activator.CreateInstance(__type, ilistWrapper);

                            //v2 only for ObservableLinkedList,List
                            // var methodInfo = list.GetType().GetMethod("AddLast",new Type[]{genericArgument});
                            //
                            // enumerator.Reset();
                            //
                            // while (enumerator.MoveNext())
                            // {
                            //     var adapter = dictionaryAdapter.Meta.Factory.GetAdapter(genericArgument,
                            //         (IDictionary) enumerator.Current);
                            //     methodInfo?.Invoke(list,new object[]{adapter});
                            // }
                        }

                        dictionaryAdapter.SetProperty(key, ref list);

                        __adapted = true;
                    }
                }

                return storedValue;
            }

            public void Initialize(PropertyDescriptor propertyDescriptor, object[] behaviors)
            {
                propertyDescriptor.Fetch = true;
            }
        }
}

Now with attributes DicitonaryAdapter will support nesting even lists.

  public interface ICharacter:IElement
    {

        [adapter.DictionaryAdapter(typeof(ICharacterProperties))]
        ICharacterProperties properties { get; set; }

      [ListOfDictionariesAdapterAttribute(typeof(List<IGoal>))]
        List<IGoal> goals { get; set; }
    }

    public interface IElement
    {
        string label { get; set; }
        long id { get; set; }
    }

...other interfaces

Hope this the way. Hope someone find helpful.

stakx commented 2 weeks ago

@winxalex, sorry for taking so long to respond. I'm going to close this issue, since it appears to be intended as documentation for other users of DictionaryAdapter, rather than an actual problem that needs further action.

Just like .NET Core 2.1, DictionaryAdapter should probably be considered legacy by now, so I am not going to suggest that you re-submit the above info as a PR to expand on DictionaryAdapter's documentation... it probably would not be worth the effort. Instead, I would suggest that people migrate away from .NET Core 2.1 (and DictionaryAdapter) to more recent platforms and make use of the tools available there to (de-) serialize objects.