Tuesday, 14 August 2018

filtering & sorting json in .net

these guys:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

via the Newtonsoft.Json nuget package.


Entity in database describing a filter:

    public partial class DisplayGroupDataFilter
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public string FilterRoot { get; set; }
        public string PropertyNameToFilterOn { get; set; }
        public string FilterItemValues { get; set; }
        public string FilterType { get; set; }
        public bool Active { get; set; }
   
        public virtual DisplayGroup DisplayGroup { get; set; }
    }

Entity in database describing a sort:

    public partial class DisplayGroupDataSort
    {
        public int Id { get; set; }
        public int GroupId { get; set; }
        public string Field { get; set; }
        public string Direction { get; set; }
        public bool Active { get; set; }
        public int Order { get; set; }
   
        public virtual DisplayGroup DisplayGroup { get; set; }
    }


the DataService:


        /// <summary>
        /// applies filters and sort to supplied json data
        /// </summary>
        /// <param name="data">the json data</param>
        /// <param name="dataFilters">collection of filters to apply to data</param>
        /// <param name="dataSorts">collection of sorts to apply to data</param>
        /// <returns></returns>
        private string ApplManipulationsToGroupData(string data, List<DisplayGroupDataFilter> dataFilters, List<DisplayGroupDataSort> dataSorts)
        {
            JObject jdata = JObject.Parse(data);
            foreach (var filter in dataFilters)
            {
                jdata = ApplyDataFilterToGroupData(jdata, filter);
            }
            if (dataSorts.Any())
            {
                ApplySortToGroupData(jdata, dataSorts);
            }
            return JsonConvert.SerializeObject(jdata);
        }


        private JObject ApplySortToGroupData(JObject data, List<DisplayGroupDataSort> dataSorts)
        {
            // get the collection we want to sort our of the data
            JArray existing = (JArray)data.SelectToken("Location.Rows");
           
            // create a custom comparer that understands how we want sort elements
            var comparer = new JObjectSortComparer(dataSorts);

            // sort the collection using our custom comparer
            var sorted = existing.AsQueryable().OrderBy(obj => (JObject)obj, comparer);
           
            // put the sorted collection back into the orginal data
            data["Location"]["Rows"] = new JArray(sorted);
           
            return data;
        }

        private JObject ApplyDataFilterToGroupData(JObject data, DisplayGroupDataFilter dataFilter)
        {
            JObject jobject = data;

            string filterRoot = dataFilter.FilterRoot; // the node we want to apply the filter at e.g. "Location.Rows";
            string tokenNameToCheck = dataFilter.PropertyNameToFilterOn; // the name of property we want to filter on e.g. "BedId";
            string filterType = dataFilter.FilterType; // the type of filter e.g. "Exclude" or "Include";
            List<string> filterItems = dataFilter.FilterItemValues.Split(',').ToList();  // new List<string> { "K2", "K3" };
            List<JToken> matchedItems = new List<JToken>(); // will contain a list of items that match our filter

            // get the collection we want to apply the filter too
            var rows = jobject.SelectToken(filterRoot);
            // find and make a collection of the items that match our filter
            foreach (var item in rows)
            {
                var value = (string)item.SelectToken(tokenNameToCheck);
                if (filterItems.Contains(value))
                {
                    matchedItems.Add(item);
                }
            }

            // remove any of our matched items
            if (filterType == "Exclude")
            {
                foreach (var item in matchedItems)
                {
                    item.Remove();
                }
            }
            // remove anything not in our matched items (i.e. only keep matches)
            if (filterType == "Include")
            {
                var nonMatches = rows.Where(i => !matchedItems.Contains(i)).ToList();
                foreach (var item in nonMatches)
                {
                    item.Remove();
                }
            }

            return jobject;
        }


The custom comparer that does the actual sorting:

    /// <summary>
    /// compares two jobjects based on a list of sorts (containing a field name and sort direction) passed into the constructor
    /// </summary>
    public class JObjectSortComparer : IComparer<JObject>
    {
        private List<DisplayGroupDataSort> requiredSorts;

        public JObjectSortComparer(List<DisplayGroupDataSort> sorts)
        {
            requiredSorts = sorts;
        }

        public int Compare(JObject a, JObject b)
        {
            return DoCompare(a, b, 0);
        }

        private int DoCompare(JObject a, JObject b, int depth)
        {
            var fieldName = requiredSorts[depth].Field;

            var compareResult = string.Compare((string)a.SelectToken(fieldName), (string)b.SelectToken(fieldName));
            if (requiredSorts[depth].Direction.ToLower() == "desc")
            {
                compareResult = compareResult * -1;
            }

            if (compareResult == 0 && depth < requiredSorts.Count - 1)
            {
                return DoCompare(a, b, depth + 1);
            }

            return compareResult;
        }
    }



No comments:

Post a Comment