Dynamically add nested property to ExpandoObject
Asked Answered
A

4

6

I'm getting a JSON object (may contain multiple levels of JSON arrays and such) which I want to translate into an ExpandoObject.

I figured out how to add simple properties to an ExpandoObject at runtime as it implements IDictionary, but how do I add nested properties (for example, something like myexpando.somelist.anotherlist.someitem) at runtime that will resolve correctly?

Edit: Currently this works for simple (first level) properties well:

var exo = new ExpandoObject() as IDictionary<String, Object>;
exo.Add(name, value);

The question is how to get the name to be nested and the ExpandoObject to resolve accordingly.

Aqualung answered 30/11, 2012 at 22:16 Comment(1)
Alex, considering your previous question, I think you need DynamicObject instead of ExpandoObject. See this and its samplesStereotomy
E
9
dynamic myexpando = new ExpandoObject();
myexpando.somelist = new ExpandoObject() as dynamic;
myexpando.somelist.anotherlist = new ExpandoObject() as dynamic;
myexpando.somelist.anotherlist.someitem = "Hey Hey There! I'm a nested value :D";
Epifocal answered 9/7, 2014 at 6:59 Comment(0)
B
1

How about doing it like this:

var exo = new ExpandoObject() as IDictionary<String, Object>;
var nested1 = new ExpandoObject() as IDictionary<String, Object>;

exo.Add("Nested1", nested1);
nested1.Add("Nested2", "value");

dynamic d = exo;
Console.WriteLine(d.Nested1.Nested2); // Outputs "value"
Billiebilling answered 30/11, 2012 at 22:29 Comment(0)
H
1

You can do this by storing a reference to the previously retrieved object, or Dictionary, when TryGetValue is called. I use a class that looks like the following:

public DynamicFile(IDictionary<string, object> dictionary)
{
    if (dictionary == null)
        throw new ArgumentNullException("dictionary");
    _dictionary = dictionary;
    _lastGetRef = _dictionary;
}

private readonly IDictionary<string, object> _dictionary;
private IDictionary<string, object> _lastGetRef;

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    if (!_dictionary.TryGetValue(binder.Name, out result))
    {
        result = null;
        return true;
    }

    var dictionary = result as IDictionary<string, object>;
    if (dictionary != null)
    {
        result = new DynamicFile(dictionary);
        _lastGetRef = dictionary;
        return true;
    }

    return true;
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
    if(_dictionary.ContainsKey(binder.Name))
        _dictionary[binder.Name] = value;
    else if (_lastGetRef.ContainsKey(binder.Name))
        _lastGetRef[binder.Name] = value;
    else
        _lastGetRef.Add(binder.Name, value);

    return true;
}

_dictionary is set by the constructor when the dynamic object is created and is then set as the immediate last reference dictionary. This works due to the fact that Dictionarys are classes and hence reference types.

To then nest correctly, you need to instantiate each dictionary at each nest level just like a multi-dimensional array. For example:

myexpando.somelist = new Dictionary<string, object>();
myexpando.somelist.anotherlist = new Dictionary<string, object>();
myexpando.somelist.anotherlist.someitem = "Hey Hey There! I'm a nested value :D";

You could probably write some code in TryGetMember that automatically adds a Dictionary when the key doesn't exist, but I didn't need that so I didn't add it.

Heddi answered 22/12, 2012 at 7:21 Comment(0)
O
0

Very easy

Extension

public static class ObjectExtension
{
    public static ExpandoObject ToExpando(this object source)
    {
        IDictionary<string, object> anonymousDictionary = new RouteValueDictionary(source);

        IDictionary<string, object> expando = new ExpandoObject();

        foreach (var item in anonymousDictionary)
            expando.Add(item);

        return (ExpandoObject)expando;
    }
}

Client Action

    public IActionResult Client(string username)
    {
        var client = HttpContext.Items["profile"] as Client;

        var model = new
        {
            client.Id,
            client.Name,
            client.Surname,
            client.Username,
            client.Photo,
            CoverPhoto = new {
               client.CoverPhoto.Source,
               client.CoverPhoto.X
            }.ToExpando(),
        }.ToExpando();

        return View(model);
    }

Client.cshtml

<img id="cover-photo" src="@Model.CoverPhoto.Source" />
Ochone answered 1/6, 2018 at 6:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.