【问题标题】:Xamarin grouped ListView: How to convert JSON into a model made for grouped ListViewXamarin 分组 ListView:如何将 JSON 转换为为分组 ListView 制作的模型
【发布时间】:2020-09-05 16:01:10
【问题描述】:

我正在将 ASP.NET Core 后端中的类别作为数组加载到 Xamarin 应用程序中,用于 ListView 的分组项目,如https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/listview/customizing-list-appearance#grouping

09-06 23:25:36.679 W/SSLCertificateSocketFactory(29717): Bypassing SSL security checks at caller's request
[0:] Newtonsoft.Json.JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'BoerPlaza.Models.Products.Category' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.
Path '[0].id', line 1, position 7.
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x003a0] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0006d] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList (System.Collections.IList list, Newtonsoft.Json.JsonReader reader, Newtonsoft.Json.Serialization.JsonArrayContract contract, Newtonsoft.Json.Serialization.JsonProperty containerProperty, System.String id) [0x00173] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, System.Object existingValue, System.String id) [0x000dc] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType, Newtonsoft.Json.Serialization.JsonContract contract, Newtonsoft.Json.Serialization.JsonProperty member, Newtonsoft.Json.Serialization.JsonContainerContract containerContract, Newtonsoft.Json.Serialization.JsonProperty containerMember, System.Object existingValue) [0x0007f] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType, System.Boolean checkAdditionalContent) [0x000db] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00054] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader reader, System.Type objectType) [0x00000] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject (System.String value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) [0x0002d] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <2073514815234917a5e8f91b0b239405>:0 
  at Newtonsoft.Json.JsonConvert.DeserializeObject[T] (System.String value) [0x00000] in <2073514815234917a5e8f91b0b239405>:0 
  at BoerPlaza.Services.CategoryDataStore.GetCategoriesParentAsync () [0x0013b] in C:\Users\QuanDar\source\repos\BoerPlaza\Frontend\BoerPlaza\BoerPlaza\Services\CategoryDataStore.cs:47 
  at BoerPlaza.ViewModels.BrowseCategoryViewModel.ExecuteLoadCategoriesCommand () [0x00068] in C:\Users\QuanDar\source\repos\BoerPlaza\Frontend\BoerPlaza\BoerPlaza\ViewModels\BrowseCategoryViewModel.cs:49 

模型的问题是你必须从 ObservableCollection 继承。所有其他模型都工作正常,只是这个我因为继承而无法工作。当我删除继承时,一切正常,但我无法在 ListView 中对我的项目进行分组。

尝试使用 List 而不是 ObservableCollection。不要紧。 Xamarin 模型:(不包括 EF Core 模型,但 JSON 应该显示可用的内容)

    public class Category : ObservableCollection<Category>
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public Category Parent { get; set; }
        public Image Image { get; set; }
        public ObservableCollection<Category> Children => this;

        public Category(string title)
        {
            this.Title = title;
        }

        public Category()
        {

        }
     }

来自后端的 JSON:

  [
      {
      "id": 1,
      "title": "Vlees",
      "description": null,
      "parent": null,
      "image": {
        "alt": "Bedrijven WordPress themes",
        "pathThumbnail": "Templates/constructie.jpg"
      },
      "children": [
        {
        "id": 5,
        "title": " Kip",
         "description": null,
         "image": {
           "alt": "Bedrijven WordPress themes",
           "pathThumbnail": "Templates/constructie.jpg"
          },
          "children": []
          },
          {
         "id": 6,
         "title": "Koe",
         "description": null,
         "image": {
           "alt": "Bedrijven WordPress themes",
           "pathThumbnail": "Templates/constructie.jpg"
         },
        "children": []
       }
      ]
      },
      {
      "id": 2,
    "title": "Groente",
    "description": null,
    "parent": null,
    "image": {
      "alt": "Bedrijven WordPress themes",
      "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": [
      {
      "id": 9,
      "title": "Boemkool",
      "description": null,
      "image": {
      "alt": "Bedrijven WordPress themes",
      "pathThumbnail": "Templates/constructie.jpg"
      },
      "children": []
    }
    ]
    },
    {
    "id": 3,
    "title": "Fruit",
    "description": null,
    "parent": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": [
    {
    "id": 10,
    "title": "Appel",
    "description": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": []
    },
    {
    "id": 11,
    "title": "Peer",
    "description": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": []
    }
    ]
    },
    {
    "id": 4,
    "title": "Kruiden",
    "description": null,
    "parent": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": [
    {
    "id": 12,
    "title": "Knoflook",
    "description": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": []
    },
    {
    "id": 13,
    "title": "Peterselie",
    "description": null,
    "image": {
    "alt": "Bedrijven WordPress themes",
    "pathThumbnail": "Templates/constructie.jpg"
    },
    "children": []
    }
    ]
    }
    ]

HTTP 服务

public async Task<IEnumerable<Category>> GetCategoriesParentAsync()
{
    if (IsConnected)
    {
        var json = await client.GetStringAsync($"api/products/categories/parent");
        return await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Category>>(json));
    }

    return this.categories;

}

视图模型: 当我自己设置数组时,一切正常。

    public ObservableCollection<Category> Categories { get; set; }
...
    public async Task ExecuteLoadCategoriesCommand()
    {
        IsBusy = true;
    this.PreviousCategoryHolder.Add(this.Categories);

    try
    {
       
        Categories.Clear();
        // This does not work and throws the error
        //var categories  = await CategoryStore.GetCategoriesParentAsync();
        //foreach (var category in categories)
        //{
        //    Categories.Add(category);
        //}
       
        // when I do it like this, it works and displays the items.
        this.Categories = new ObservableCollection<Category> 
        {
            new Category("Vlees")
            {
                new Category("kip"),
                new Category("kip file")

            },
            new Category("Groente")
            {
                new Category("Appel"),
                new Category("Banaan")
            }
        };
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
    }
    finally
    {
        IsBusy = false;
    }
}

xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage  
             xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             x:Class="BoerPlaza.Views.BrowsePage">
    <!-- binding context is in code behind -->
    <ContentPage.Content>
        <ListView x:Name ="categoriesListView" 
                  IsGroupingEnabled="true" 
                  ItemTapped="Handle_ItemTapped"
                  CachingStrategy="RecycleElement"
                  >
            <ListView.GroupHeaderTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Title}"/>
                </DataTemplate>
            </ListView.GroupHeaderTemplate>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Title}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

谢谢!

【问题讨论】:

  • 请使用 json2csharp.com 检查您的模型。首先,您的模型属性都不匹配您的 json 中使用的命名约定
  • 正在努力实现这一目标:docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/…。但随后使其与 JSON 一起工作。 json2csharp.com 只是创建了许多模型,我不需要嵌套的 ViewList。
  • 您可以根据需要修改模型以消除嵌套,但这不会改变您发布的代码不符合 json 中使用的命名约定的事实。

标签: c# xamarin xamarin.forms xamarin.android asp.net-core-webapi


【解决方案1】:

您可以创建一个新的视图模型,然后将解析后的数据设置给它。

例如:

GroupViewModel

public class GroupViewModel:ObservableCollection<Category>
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public Category Parent { get; set; }
    //public Image Image { get; set; }
    public ObservableCollection<Category> Children => this;

    public GroupViewModel(string title)
    {
        this.Title = title;
    }
}

类别

public class Category 
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    //public Category parent { get; set; }
    public List<Category> Children;
 }

设置ItemSource

var data = JsonConvert.DeserializeObject<List<Category>>(json);// the json you request from service
ObservableCollection<GroupViewModel> groupList = new ObservableCollection<GroupViewModel>();
foreach (var item in data)
{
       GroupViewModel children = new GroupViewModel(item.Title);
       foreach (var i in item.Children)
       {
           children.Add(i);
       }
       groupList.Add(children);
 }
 categoriesListView.ItemsSource = groupList;

【讨论】:

    【解决方案2】:

    使用 NewtonSoft nuget (https://www.nuget.org/packages/Newtonsoft.Json/),您可以定义 json 属性和模型属性之间的双射。就像这样:

     public class Product
    {
        [JsonProperty("id")]
        public int Id { get; set; }
    
        [JsonProperty("name")]
        public string Name { get; set; }
    
        [JsonProperty("price")]
        public decimal Price { get; set; }
    
        [JsonProperty("imageUrl")]
        public string ImageUrl { get; set; }
    }
    

    那么您的 HTTP 服务必须如下所示:

    var response = await client.GetAsync(your_url);
    var result = await response.Content.ReadAsStringAsync();
    var list = JsonConvert.DeserializeObject<List<Product>>(result);
    

    您的后端端点应如下所示:

    public async Task<IActionResult> GetProductsAsync()
        {
            var dataContext = productRepository.GetAll();
            return Ok(await dataContext.ToListAsync());
        }
    

    我使用的是存储库模式设计,你可以直接处理 DataContext DB

    【讨论】:

    猜你喜欢
    • 2015-10-28
    • 2020-04-24
    • 2019-02-15
    • 2017-05-14
    • 2020-02-07
    • 2019-05-18
    • 1970-01-01
    相关资源
    最近更新 更多