Search code examples
c#jsonxamarin.formsdata-bindingpicker

Xamarin forms read local json file and display in picker


I am trying to parse a json file of contacts into a list and display that list to the user in a picker on my page displaying the contact names.

I have a json file in the root of my project called "contacts.json" and its build action is set to embedded resource.

my contacts.json file

{
  "contacts": [
    {
      "name": "JOE",
      "email": "name@handle",
      "phoneNumber": "123-456-7890"
    },
    {
      "name": "JYM",
      "email": "name@handle",
      "phoneNumber": "123-456-7890"
    }
  ]
}

my contact model:

    public partial class RootObject
    {
        [JsonProperty("contacts")]
        public List<Contact> Contacts { get; set; }
    }

    public partial class Contact
    {
        [JsonProperty("name")]
        public string Name { get; set; }

        [JsonProperty("email")]
        public string Email { get; set; }

        [JsonProperty("phoneNumber")]
        public string PhoneNumber { get; set; }
    }

my page viewmodel where i implement a json parser

    public partial class Page10 : BaseViewModel
    {
        private List<Contact> _contacts;
        public List<InternalContact> contacts
        {
            get { return _contacts; }
            set
            {
                _contacts = value;
                OnPropertyChanged("contacts");
            }
        }

        public Page10()
        {
            Title = "Spill Info";
            contacts = GetJsonData();
        }

        private List<Contact> GetJsonData()
        {
            string jsonFileName = "contacts.json";
            RootObject ObjContactList = new RootObject();


            var assembly = typeof(Page10).GetTypeInfo().Assembly;
            Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{jsonFileName}");
            using (var reader = new System.IO.StreamReader(stream))
            {
                var jsonString = reader.ReadToEnd();

                //Converting JSON Array Objects into generic list    
                ObjContactList = JsonConvert.DeserializeObject<RootObject>(jsonString);
            }   
            return ObjContactList.Contacts;
        }
    }

my baseViewModel

public class BaseViewModel : INotifyPropertyChanged
    {
        string title = string.Empty;
        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        protected bool SetProperty<T>(ref T backingStore, T value,
            [CallerMemberName]string propertyName = "",
            Action onChanged = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;

            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            return true;
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }

my page.xaml.cs

public partial class Page10 : ContentPage
    {
        public Page10()
        {
            InitializeComponent();
            this.BindingContext = new contactviewmodel();
        }
    }

my page xaml

<ContentPage.Content>
        <StackLayout>
            <Picker Title="contacts" ItemsSource="{Binding contacts}" ItemDisplayBinding="{Binding Name}"/>    
        </StackLayout>
    </ContentPage.Content>

After trying the above i get an empty picker when selected but i would expect to have seen JOE and JYM in the picker.

edit 1: I managed to get them to display in a list so i fiddled with trying to get them into a picker from there but i am only getting a list of my object types and not names in the picker. updated code to reflect changes. image of phone w/ contact list view and picker (cant embed pictures yet, not enough rep).

edit2: modified code to show @Cherry Bu- MSFT 's implementation


Solution

  • According to your description, I do one sample that you can take a look:

    public partial class Page10 : ContentPage, INotifyPropertyChanged
    {
        private List<Contact> _contacts;
        public List<Contact> contacts
        {
            get { return _contacts; }
            set
            {
                _contacts = value;
                RaisePropertyChanged("contacts");
    
            }
        }
    
        public Page10()
        {
            InitializeComponent();
    
    
            contacts = GetJsonData();
    
    
            this.BindingContext = this;
        }
    
       private List<Contact> GetJsonData()
        {
            string jsonFileName = "contacts.json";
            ContactList ObjContactList = new ContactList();
    
    
            var assembly = typeof(Page10).GetTypeInfo().Assembly;
            Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{jsonFileName}");
            using (var reader = new System.IO.StreamReader(stream))
            {
                var jsonString = reader.ReadToEnd();
    
                //Converting JSON Array Objects into generic list    
                ObjContactList = JsonConvert.DeserializeObject<ContactList>(jsonString);
            }
    
            return ObjContactList.contacts;
        }
    
    
        public event PropertyChangedEventHandler PropertyChanged;      
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    public partial class ContactList
    {
        [JsonProperty("contacts")]
        public List<Contact> contacts { get; set; }
    }
    
    public partial class Contact
    {
        [JsonProperty("name")]
        public string Name { get; set; }
    
        [JsonProperty("email")]
        public string Email { get; set; }
    
        [JsonProperty("phoneNumber")]
        public string PhoneNumber { get; set; }
    }
    
      <StackLayout>
            <ListView x:Name="MyListView" ItemsSource="{Binding contacts}">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextCell Detail="{Binding Email}" Text="{Binding Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
    
            <Picker
                x:Name="MyPicker"
                ItemDisplayBinding="{Binding Name}"
                ItemsSource="{Binding contacts}" />
        </StackLayout>
    

    Please don't forget to implement INotifyPropertychanged interface to nofity data updated.

    Update:

    If you want to get local Json file and display data in ListView using mvvm, please take a look the following code, I use Mvvm Mode.

     public partial class Page10 : ContentPage
    {
        public Page10()
        {
            InitializeComponent();
    
            this.BindingContext = new contactviewmodel();
        }   
    
    }
    
    public class contactviewmodel:ViewModelBase
    {
        private List<Contact> _contacts;
        public List<Contact> contacts
        {
            get { return _contacts; }
            set
            {
                _contacts = value;
                RaisePropertyChanged("contacts");
    
            }
        }
    
        public contactviewmodel()
        {
            contacts = GetJsonData();
        }
        private List<Contact> GetJsonData()
        {
            string jsonFileName = "contacts.json";
            ContactList ObjContactList = new ContactList();
    
    
            var assembly = typeof(Page10).GetTypeInfo().Assembly;
            Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.{jsonFileName}");
            using (var reader = new System.IO.StreamReader(stream))
            {
                var jsonString = reader.ReadToEnd();
    
                //Converting JSON Array Objects into generic list    
                ObjContactList = JsonConvert.DeserializeObject<ContactList>(jsonString);
            }
            //Binding listview with json string     
            return ObjContactList.contacts;
        }
    
    }
    

    The ViewModelBase is the class that implementing INotifyPropertyChanged:

     public class ViewModelBase : INotifyPropertyChanged
    {
    
        public event PropertyChangedEventHandler PropertyChanged;      
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    Update again:

    You can use the following code to get Json file.

     private void LoadData()
        {
            var assembly = typeof(Page10).GetTypeInfo().Assembly;
            foreach (var res in assembly.GetManifestResourceNames())
            {
                if (res.Contains("contacts1.json"))
                {
                    Stream stream = assembly.GetManifestResourceStream(res);
    
                    using (var reader = new StreamReader(stream))
                    {
                        string data = "";
                        while ((data = reader.ReadLine()) != null)
                        {
    
                        }
                    }
                }
            }
        }
    

    enter image description here