public bool FromJSON(JObject jo)        // rechecked 31/7/2020 with quickjson
        {
            try
            {
                Clear();

                JArray jf = (JArray)jo["FilterSet"];

                foreach (JObject j in jf)
                {
                    // verified 31/7/2020 with QuickJSON   If object not present, returns JNotPresent and Str() returns default
                    string evname = (string)j["EventName"];
                    Condition.LogicalCondition ftinner = (Condition.LogicalCondition)Enum.Parse(typeof(Condition.LogicalCondition), j["ICond"].Str("Or"));
                    Condition.LogicalCondition ftouter = (Condition.LogicalCondition)Enum.Parse(typeof(Condition.LogicalCondition), j["OCond"].Str("Or"));
                    string act  = j["Actions"].Str();
                    string actd = j["ActionData"].Str();

                    JArray filset = (JArray)j["Filters"];

                    List <ConditionEntry> fieldlist = new List <ConditionEntry>();

                    foreach (JObject j2 in filset)
                    {
                        string item      = (string)j2["Item"];
                        string content   = (string)j2["Content"];
                        string matchtype = (string)j2["Matchtype"];

                        fieldlist.Add(new ConditionEntry()
                        {
                            ItemName       = item,
                            MatchString    = content,
                            MatchCondition = (ConditionEntry.MatchType)Enum.Parse(typeof(ConditionEntry.MatchType), matchtype)
                        });
                    }

                    conditionlist.Add(new Condition()
                    {
                        EventName      = evname,
                        InnerCondition = ftinner,
                        OuterCondition = ftouter,
                        Fields         = fieldlist,
                        Action         = act,
                        ActionVars     = new Variables(actd, Variables.FromMode.MultiEntryComma)
                    });
                }

                return(true);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Bad condition JSON:" + ex);
            }

            return(false);
        }
        public string Read(string line)         // decode a set of multi conditions (<cond> Or <cond>) Outer (<cond> And <cond>) etc
        {
            StringParser sp = new StringParser(line);

            bool multi = false;

            string delimchars = " ";

            if (sp.IsCharMoveOn('('))
            {
                multi      = true;
                delimchars = ") ";
            }

            List <Condition> cllist = new List <Condition>();

            Condition.LogicalCondition outercond = Condition.LogicalCondition.Or;         // first outer condition is ignored in a list.  Or is the default.

            while (true)
            {
                Condition c = new Condition();

                string err = c.Read(sp, delimchars: delimchars);
                if (err.Length > 0)
                {
                    return(err);
                }

                c.OuterCondition = outercond;
                cllist.Add(c);            // add..

                if (sp.IsCharMoveOn(')')) // if closing bracket..
                {
                    if (!multi)
                    {
                        return("Closing condition bracket found but no opening bracket present");
                    }

                    if (sp.IsEOL)  // EOL, end of  (cond..cond) outercond ( cond cond)
                    {
                        Clear();
                        conditionlist = cllist;
                        return(null);
                    }
                    else
                    {
                        err = Condition.GetLogicalCondition(sp, delimchars, out outercond);
                        if (err.Length > 0)
                        {
                            return(err + " for outer condition");
                        }

                        if (!sp.IsCharMoveOn('(')) // must have another (
                        {
                            return("Missing opening bracket in multiple condition list after " + outercond.ToString());
                        }
                    }
                }
                else if (sp.IsEOL) // last condition
                {
                    if (multi)
                    {
                        return("Missing closing braket in multiple condition list");
                    }

                    Clear();
                    conditionlist = cllist;
                    return(null);
                }
                else
                {
                    return("Extra characters beyond expression");
                }
            }
        }