Displaying structured textual data from JSON, into a listview using cell and two labels

User2020User2020 Member
edited January 19 in Xamarin.Forms

Hi,

My apologies this maybe a long one. I am building an application which will display some structured text into a list view. The text comes from a JSON file and I am using Newton JSON to parse that. The structure of the JSON file is as follows. Generally the depth will not exceed more than 3. I am only displaying the POINTS section of data, everything is selected before (so users will click on Parts -> Sections -> Then points will show). The points is the primary text which needs a nice structure in a list view cell.

{
    "Name": "TITLE",
    "Year": 1400,
    "Created": "2019-12-27",
    "Updated": "2019-12-27",
    "Parts": [
        {
            "Part": "Part 10",
            "Name": "SOME NAME",
            "Sections": [
                {
                    "Name": "SOME SECTIONS WITH CONTENT",
                    "Section": "1",
                    "Points": [
                        {
                            "Point": "(1)",
                            "Content": "SOME NESTED CONTENT",
                            "Children": [
                                {
                                    "Point": "(A)",
                                    "Content": "SOME NESTED NESTED CONTENT",
                                    "Children": []
                                },
                            ]
                        },
                        {
                            "Point": "(2)",
                            "Content": "SOME NESTED CONTENT",
                            "Children": []
                        },
                        {
                            "Point": "(3)",
                            "Content": "SOME NESTED CONTENT",
                            "Children": []
                        }
                    ]
                }
            ]}
    ]
}

Currently the XAML looks like this.

        <ListView x:Name="ListPoints" CachingStrategy="RecycleElement" HasUnevenRows="True">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout x:Name="Content" Orientation="Horizontal" Padding="10">
                                <Label x:Name="ListPoint" Text="{Binding Point}" MinimumWidthRequest="30"/>
                                <Label x:Name="ListContent" Text="{Binding Content}"/>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

However, this only shows the first level, and not the children or nested children of POINTS. What I want to do is dynamically added the stack layout for each point. How could I accomplish this?

The C# code currently assigns the points collection like this.
ListPoints.ItemsSource = sections.Points;

I have been trying to solve this issue for the past 3 days now to now avail, I cannot seem to find a decent solution. I have partially got it working using Android and a custom render, but that messes things up on scrolling, I am trying to get a full solution in Xamarin Forms, hopefully there should be a way of doing what I want.

The screenshot below shows what I want to achieve, that was doing using Xamarin.Android using custom list view render, it caused issues so looking for a full solution in Forms. The content from point (1) to (iii) is one big view cell, using multiple labels to define points and content. That's what I want, currently I only get the first point and content when using Xamarin Forms.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    I think we should start from the json string part. I made a new string focusing on the Sections because you said all you needed are located in that key:

    var jsonStr = @" [                                   
                        {
                            ""Name"": ""SOME SECTIONS WITH CONTENT"",
                            ""Section"": ""1"",
                            ""Points"": [
                                {
                                    ""Point"": ""(1)"",
                                    ""Content"": ""SOME NESTED CONTENT"",
                                    ""Children"": [
                                        {
                                            ""Point"": ""(A)"",
                                            ""Content"": ""SOME NESTED NESTED CONTENT"",
                                            ""Children"": []
                                        },
                                    ]
                                },
                                {
                                    ""Point"": ""(2)"",
                                    ""Content"": ""SOME NESTED CONTENT"",
                                    ""Children"": []
                                },
                                {
                                    ""Point"": ""(3)"",
                                    ""Content"": ""SOME NESTED CONTENT"",
                                    ""Children"": []
                                }
                            ]
                        }
                        ] ";
    

    I made three model classes to access these information:

    public class MainModel
    {
        public string Section { set; get; }
        public string Name { set; get; }
        public List<NestedModel> Points { set; get; }
    }
    public class NestedModel
    {
        public string Point { set; get; }
        public string Content { set; get; }
        public List<NestedModel> Children { set; get; }
    }
    
    public class ItemModel
    {
        public string Point { set; get; }
        public string Content { set; get; }
    }
    

    And then deserialize the string like:

    var models = JsonConvert.DeserializeObject<List<MainModel>>(jsonStr);
    foreach(MainModel model in models)
    {
        iterate(model.Points);
    }
    // bind your listview's items source to items here.
    
    List<ItemModel> items = new List<ItemModel>();
    void iterate(List<NestedModel> models)
    {           
        foreach(NestedModel nested in models)
        {
            items.Add(new ItemModel { Point = nested.Point, Content = nested.Content });
            if (nested.Children.Count != 0)
            {
                iterate(nested.Children);
            }              
        }            
    }
    

    We will get 4 items after this iterating. At last, we could consume the ItemModel directly to display usful information.
    Here is my sample.

Sign In or Register to comment.