Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

asp5 IConfigurationRoot get json array

I am trying to supply some additional configuration data to my asp 5 mvc 6 web app ("rc1-final" libraries) to be deployed on Azure. Part of this additional data consists of an array of data in a json file. I can add this json file to my configuration, and while debugging if I watch the configuration object I can browse to the provider entry and see it does in fact contact the array of data I'm looking for.

However, trying Configuration["extraData"] yields null (as the "value" of that property is null, even though it clearly contains the array of elements.

I have no problem accessing single values that are stored in json config (such as my database connection string, and other values.) It just seems as though json data in array format is not supported?

See the following sample json file (which validates according to jsonlint and seems fine to me)...

//extraData.json
{
  "ExtraData": [
    {
      "Id": 1,
      "Description": "The first identifier"
    },
    {
      "Id": 2,
      "Description": "The second identifier"
    },
    {
      "Id": 3,
      "Description": "The third identifier"
    }
  ]
}

...and the following sample Startup.cs...

//Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Data.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Newtonsoft.Json.Linq;
using System.IO;
using Microsoft.AspNet.Authentication.Cookies;
using System.Net;


namespace MyProj {
    public class Startup {
        public static IConfigurationRoot Configuration;

        public Startup(IHostingEnvironment env) {
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddJsonFile("extraData.json")
                .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        public void Configure(
            IApplicationBuilder app,
            IHostingEnvironment env,
            MyDbSeed seed,
            ILoggerFactory logging) {

            dynamic xData = JObject.Parse("ExtraData"); //VALUE IS NULL, THROWS EX
            var myList = JsonConvert
                .DeserializeObject<List<MyID>>(
                    xData.ExtraData.ToString()
                );
            //Do something important with this list
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }

    class MyID {
        public int ID { get; set; }
        public string Description { get; set; }
    }
}

What I've been having to do instead (which seems hackish, especially given the handy Configuration system now available)...

dynamic xData = JObject.Parse(
    File.OpenText(
        env.MapPath(
            @"..\extraData.json"
        )
    ).ReadToEnd()
);
var xList = JsonConvert
    .DeserializeObject<List<MyID>>(
        xData.ExtraData.ToString()
    );
//Do something important with the list, eg...
var o = new MyClass(xList);

I'm also uncertain as to the support on azure to do file io like this.

I found this resource which suggests that array/IEnum is supported, but it is just using an in-mem provider...
http://github.com/aspnet/Configuration/blob/dev/test/Microsoft.Extensions.Configuration.Binder.Test/ConfigurationCollectionBindingTests.cs

This resource is the documentation from MS...
http://docs.asp.net/en/latest/fundamentals/configuration.html

Some related questions that have turned up while searching since posted (not 1:1 related to my issue necessarily, but could prove useful for others searching)
How to use ConfigurationBinder in Configure method of startup.cs
Retrieve sections from config.json in ASP.NET 5
ASP.NET 5 (vNext) - Getting a Configuration Setting

like image 261
t.j. Avatar asked Dec 04 '15 07:12

t.j.


4 Answers

We can now use GetSection with GetChildren and some LINQ.

// appsettings.json
{
    "SomeArray": [
        "Foo", 
        "Bar",
        "Baz"
    ]
}

// Startup.cs
var someArray = configuration
    .GetSection("SomeArray")
    .GetChildren()
    .Select(x => x.Value)
    .ToArray();

There is also a demo in the Configuration repository's ArrayTest.

like image 176
Shaun Luttin Avatar answered Oct 22 '22 13:10

Shaun Luttin


try this out:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.OptionsModel;
using System.Collections.Generic;

namespace WebApplication1
{
    public class Startup
    {
        public Startup(IHostingEnvironment env)
        {
            // Set up configuration sources.
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .AddJsonFile("extraData.json")
                .AddEnvironmentVariables();

            Configuration = builder.Build();
        }

        public IConfigurationRoot Configuration { get; set; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOptions();
            services.Configure<List<ExtraData>>(Configuration.GetSection("ExtraData"));
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            List<ExtraData> result = app.ApplicationServices.GetRequiredService<IOptions<List<ExtraData>>>().Value;

            // you should get what you need. It works in my computer with rc1 update 1
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }

    public class ExtraData
    {
        public int Id { get; set; }
        public string Description { get; set; }
    }
}

EDIT

If compiler complains services.Configure<List<ExtraData>>(Configuration.GetSection("ExtraData")), please add a NuGet reference to Microsoft.Extensions.Options.ConfigurationExtensions

You may also inject it into constructor:

// using Microsoft.Extensions.Options;
public class HomeController : Controller
{
    private List<ExtraData> _extraData;
    public HomeController(IOptions<List<ExtraData>> extraData)
    {
        _extraData = extraData.Value;
    }
}
like image 45
Ricky Avatar answered Oct 22 '22 15:10

Ricky


You can retrieve settings this way:

var extraDataSection = configuration.GetSection("ExtraData");
foreach (IConfigurationSection section in extraDataSection.GetChildren())
{
    var id = section.GetValue<int>("Id");
    var description = section.GetValue<string>("Description");
}
like image 42
Dmitry Pavlov Avatar answered Oct 22 '22 15:10

Dmitry Pavlov


With the latest versions I was able to solve this without any additional NuGet packages:

int extraDataCount = 0;
string id = Configuration[GetId(extraDataCount)];
string description = Configuration[GetDescription(extraDataCount)];

var extraDataList = new List<MyID>();
while(id != null && description != null)
{
    extraDataList.Add(new MyID { ID = id, Description = description });
    extraDataCount++;
    id = Configuration[GetId(extraDataCount)];
    description = Configuration[GetDescription(extraDataCount)];
}

private string GetId(int extraDataCount)
{
    return "ExtraData:" + extraDataCount + ":Id";
}

private string GetDescription(int extraDataCount)
{
    return "ExtraData:" + extraDataCount + ":Description";
}
like image 21
Yusuf Bhagat Avatar answered Oct 22 '22 14:10

Yusuf Bhagat