3

I have a piece of code to add a new Rule to an Item field but i can't work out how to convert this back to XML, Can anyone point me in the right direction? :)

The RuleFactory doesn't appear to offer anything but there must be something .. ?

            var rulesField = item.Fields[ExtendedShippingProviderLookupValueFields.DisplayRulesFieldId];

            RuleList<ShippingRuleContext> rules = RuleFactory.GetRules<ShippingRuleContext>(rulesField);

            var newRule = new Rule<ShippingRuleContext>();
            newRule.Actions.Add(new HideShippingOption<ShippingRuleContext>());
            var condition = new ShippingCountryCondition<ShippingRuleContext>();
            condition.CountryCode = "NZ";
            newRule.Condition = condition;
            rules.Add(newRule);

            item[ExtendedShippingProviderLookupValueFields.DisplayRulesFieldId] = rules.ToString();

I've tried a simple serialization but that does not work either.

item[ExtendedShippingProviderLookupValueFields.DisplayRulesFieldId] = GetXML(rules);

   private string GetXML(RuleList<ShippingRuleContext> rules)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(RuleList<ShippingRuleContext>));
        var stream = new MemoryStream();
        var streamWriter = new StreamWriter(stream, System.Text.Encoding.UTF8);
        serializer.Serialize(streamWriter, rules);
        return Encoding.ASCII.GetString(stream.ToArray());
    }

Sitecore's interface generates XML in this format

<ruleset>  
  <rule uid="{01FC00E0-1F84-44D3-80BF-3D003137CEC3}">    
  <conditions>      
    <condition id="{F9878A36-9395-443B-9D7D-ACF721639757}"              uid="B2B44316D8C248238399184DF1E2207F" CountryCode="NZ" />    
    </conditions>    
  <actions>      
    <action id="{F65EE2F1-3BB7-45D0-8B07-0CDEFDD097E6}" uid="779A10F1CC4C4B79B4EE5C261C4BF83B" />    
  </actions>  
 </rule>
</ruleset>

The serialization of the objects generates this

<RuleListOfShippingRuleContext xmlns:xsd="http://www.w3.org/2001/XMLSchema"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Name>/sitecore/content/Healthspan/Business Catalog/Shipping Providers/EURO COURIER</Name>  
</RuleListOfShippingRuleContext>

so as you can see nothing like it! I was hoping there would be a helper class like some of the other XML based field types but i've been unable to find one.

For completeness this is my final code solution following Zachary's Direction

            var ruleDef = new RulesDefinition(item[ExtendedShippingProviderLookupValueFields.DisplayRulesFieldId]);

            Guid ruleGuid = Guid.NewGuid();
            var rule = ruleDef.AddRule(ruleGuid);
            var condition = ruleDef.AddCondition(ruleGuid,ID.Parse("{F9878A36-9395-443B-9D7D-ACF721639757}"));
            XAttribute attribute = new XAttribute("CountryCode","NZ");
            condition.Add(attribute);
            var action = ruleDef.AddAction(ruleGuid, ID.Parse("{F65EE2F1-3BB7-45D0-8B07-0CDEFDD097E6}"));

            item[ExtendedShippingProviderLookupValueFields.DisplayRulesFieldId] = ruleDef.ToString();
  • did you try Sitecore.Rules.RuleFactory.ParseRules<T>(Database database, string rulesXml) where T : RuleContext – Balaji Kuppuswamy Mar 14 '17 at 12:45
  • I did look at this but ParseRules generates a RuleList from rulesXML not the reverse unfortunately. – Stuart Bronk Mar 14 '17 at 12:50
  • Can you provide an example of the xml? Does the xml you want be like how Sitecore stores it? – Hishaam Namooya Mar 14 '17 at 16:47
  • I've added the XML to the original post – Stuart Bronk Mar 14 '17 at 17:06
  • @StuartBronk, if you require the XML to be as Sitecore's interface XML Format, please see my answer below – Hishaam Namooya Mar 14 '17 at 17:08
3

The Short Answer

The short answer is that you can't convert a RuleList<T> back to XML, in Sitecore, using the Sitecore API. However, there is a class, the Sitecore.Rules.RuleDefinition class, that you can use to manipulate Rule XML.

If what you really want to do is convert a RuleList<T> back to XML then you could reverse engineer the Sitecore.Rules.DefaultRuleFactory class' methods, starting with GetRule<T>(Database, XElement). You would then have to reverse engineer each of the other methods in the class that are called from the GetRule<T> method's call stack (which is pretty much all of them). IMHO, reverse engineering the DefaultRuleFactory class' methods would be frowned upon and inadvisable, not to mention the fact that you would be taking on a lot of excess risk and responsibility.

Using the RuleDefinition Class to Manipulate Rules and Rule XML

What I believe you are looking for is the Sitecore.Rules.RuleDefinition class. It's not exactly what you are looking for, but it is the closest thing to it that you will find.

Using the RuleDefinition Class

Sitecore uses the RuleDefinition class to manage the XML manipulation of the XML for Rules. Based on the OP and your comments, what you would like to do in code is take any rules that have been created on a Rule field, add in a new rule and save it to the field.

What you would do is something like the following:

var ruleDef = new RuleDefinition(item["MyRulesField"]);

// manipulate the RuleDefinition using one of its methods, e.g:
ruleDef.AddRule(...)
ruleDef.AddAction(...)
ruleDef.AddCondition(...)
ruleDef.Move(...)
ruleDef.MoveRule(...)
ruleDef.RemoveAction(...)
ruleDef.RemoveCondition(...)
ruleDef.RemoveRule(...)
...and dozens more...

var xml = ruleDef.ToString();

There are many other methods than those I listed and most of the methods on that class (including the ones I listed) have several overloads for you to use.

Implications from the API

The Sitecore API doesn't actually provide a way to take a RuleList<T> and convert it back into XML. Given the existence and use of the RuleDefinition class, the implication is that developers should not be retrieving and parsing a RuleList<T> and then converting that back into XML. Even the Sitecore Client doesn't work this way.

Instead, developers should start with the XML - the raw rule field value - and then use the API to add/remove/manipulate rules, conditions and actions from that XML value. Once complete, the resulting XML value should be retrieved by calling RuleDefinition.ToString() method, which returns a string XML value that can be saved back to the field.

Summing up

Without knowing the specifics of what and how you are trying to manipulate your rule, I simply cannot point you to a more specific method. However, I think that this should point you in the right direction and give you a pretty good head start. Even if you have to rewrite some of your current implementation to accommodate this, IMHO this is the only feasible option as far as manipulating Rule XML goes.

Good luck!

  • Thanks for your input really appreciated and well written, i'd taken a look at the RuleDefinition class and could see it had potential but hadn't made the leap. – Stuart Bronk Mar 14 '17 at 19:29
  • No problem - it's always good to confirm with the community ;) especially when you might have to rewrite existing code. – Zachary Kniebel Mar 14 '17 at 19:30
  • Thanks for the direction again, i've added my final working code solution to my question. :) – Stuart Bronk Mar 15 '17 at 9:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.