Search code examples
asp.netxmlwebformsrepeater

How to access XML subnode in repeater


I'm am trying to figure out how to access a subnode in serialized xml data using a repeater in my asp.net web form application. I can access all the xml element data on the first layer, but not on the second (Benefits).

My issue is that I cannot access Item.Benefits.BenefitImage or Item.Benefits.Benefit within the repeater.

Any pointer would be greatly appreciated.

Here is a snippet of my xml data:

<Category Key="acid" Rank="30">
  <Intro> ... </Intro>
  <Description> ... </Description>
  <Benefits>
    <Benefit Key="promote_texture">Promote smoother skin texture</Benefit>  
    <Benefit Key="promote_tone">Promote even skin tone</Benefit>
    <Benefit Key="enhance_radiance">Enhance skin radiance</Benefit>      
  </Benefits>
</Category>

Here is a snippet of the repeater in the .ASPX page:

<asp:Panel runat="server" CssClass="benefits-wrapper">
  <asp:Repeater ID="BenefitsList" runat="server" ItemType="OrdinarySite.Models.ProductCategory" SelectMethod="CategoryList_GetData">
    <ItemTemplate>
      <div class="benefits">                                 
        <div class="benefit">
           <asp:Image runat="server" CssClass="benefit-img" ImageUrl="<%# Item.Benefits.BenefitImage %>" />
           <asp:Label runat="server" CssClass="benefit-desc" Text="<%# Item.Benefits.Benefit %>"></asp:Label>
        </div>                                                               
      </div>
      <div class="benefits-copy">
        <asp:Label runat="server" CssClass="heading" Text="<%# Item.Name %>"></asp:Label>
        <p><%# Item.Description %></p>
      </div>
    </ItemTemplate>
  </asp:Repeater>
</asp:Panel>

Here is a snippet of the repeater in the .ASPX.CS page:

public partial class Category : BasePage
{
    public string categoryKey;

    protected void Page_Load(object sender, EventArgs e)
    {
        var key = (string)RouteData.Values["key"];

        categoryKey = key;

        if (categoryKey != null) {
            this.Title = categoryKey;
        } else {
            Response.Redirect("~/", true);
        }
    }

    public IEnumerable<ProductCategory> CategoryList_GetData() => CacheObject.Categories.Where(x => x.Key == categoryKey);        
}

Here is a snippet of the the class:

namespace Site.Models
{
[Serializable]
public class ProductCategory : IComparable<ProductCategory>
{
    [XmlAttribute]
    public string Key { get; set; }
    [XmlAttribute]
    public int Rank { get; set; }
    [XmlAttribute]
    public string Naming { get; set; }
    [XmlIgnore]
    public List<Product> Products { get; set; }
    public string Name => Resources.ProductCategory.ResourceManager.GetString(Key);
    public int CompareTo(ProductCategory other) => this.Rank - other.Rank;
    public static string XmlFileName => HostingEnvironment.MapPath("~/App_Data/categories.xml");
    public string Intro { get; set; }
    public string Description { get; set; }

    [XmlArray("Benefits"), XmlArrayItem("Benefit")]
    public List<BenefitsList> Benefits { get; set; }

    [Serializable]
    public class BenefitsList
    {

        [XmlAttribute]
        public string Key { get; set; }
        [XmlElement]
        public string Benefit { get; set; }

        public string BenefitImage => $"~/Images/category/benefits/{Key}.svg";
    }       

    public static List<ProductCategory> LoadXmlData( List<Product> products )
    {
        var cats = SerializerSupport.DeserializeList<ProductCategory>( XmlFileName );
        foreach( var c in cats )
        {
            c.Products = products
                .Where( x => x.Details != null && string.Equals( c.Key, x.Details.CategoryKey, StringComparison.OrdinalIgnoreCase ) )
                .OrderBy(x => x.Details.Title).ToList();
            c.Products.ForEach( x => x.Category = c );
        }

        cats.Sort();
        return cats;
    }
}

Solution

  • You need to use nested repeater to display Benefits within the repeater as follows. See: Nested repeater.

    <asp:Panel runat="server" CssClass="benefits-wrapper">
      <asp:Repeater ID="BenefitsList" runat="server" ItemType="OrdinarySite.Models.ProductCategory" SelectMethod="CategoryList_GetData">
        <ItemTemplate>
          <div class="benefits">   
            <asp:Repeater runat="server" DataSource='<%# Eval("Item.Benefits") %>'>
                <div class="benefit">
                   <asp:Image runat="server" CssClass="benefit-img" ImageUrl="<%# BenefitImage %>" />
                   <asp:Label runat="server" CssClass="benefit-desc" Text="<%# Benefit %>"></asp:Label>
                </div>  
            </asp:Repeater>                                                           
          </div>
          <div class="benefits-copy">
            <asp:Label runat="server" CssClass="heading" Text="<%# Item.Name %>"></asp:Label>
            <p><%# Item.Description %></p>
          </div>
        </ItemTemplate>
      </asp:Repeater>
    </asp:Panel>