0

问题

使用 C#,我需要遍历一个已转换为ExpandoObjectfrom的对象,XML并将任何“价格”属性替换为新值。

这个对象是非常非结构化的并且有很多层的嵌套节点(实际上是嵌套的 ExpandoObjects)。更具体地说,层次结构可能如下所示:

产品 => 价格、数量、配件

每个配件都可能有价格和数量,并且本身可能有配件,这就是我需要递归的原因。

到目前为止我所拥有的

  public ExpandoObject UpdatePricing(ExpandoObject exp)
    {
        //Is there a price property?
        var hasPrice = exp.Any(a => a.Key == "price");
        if (hasPrice)
        {
            //update price here
            exp.price = 0; //Some other price
        }

        //Now loop through the whole object.  If any of the properties contain an expando, then call this method again
        foreach (var kvp in exp)
        {
            if (kvp.Value is ExpandoObject)
            {
                //THIS CODE IS NO GOOD BECAUSE kvp.Value has no setter!!
                kvp.Value = UpdatePricing(kvp.Value);
            }
        }

        return exp;
    }

我遇到的问题是kvp.Value没有设置器,所以我不能递归地运行这个方法。

任何建议表示赞赏。谢谢!

4

3 回答 3

2

我对 ExpandoObject 了解不多。但是像大多数字典实现一样,我假设通常如果您希望将键值对更新为具有不同的值,则需要通过字典接口。

请注意,在您枚举其内容时(可能)将不允许您修改字典。因此,您需要构建要更新的元素列表,并在单独的操作中执行此操作。

例如:

List<string> keysToUpdate = new List<string>();

foreach (var kvp in exp)
{
    if (kvp.Value is ExpandoObject)
    {
        keysToUpdate.Add(kvp.Key);
    }
}

foreach (string key in keysToUpdate)
{
    exp[key] = UpdatePricing(exp[key]);
}

您还可以将整个 KeyValuePair 值保留在列表中,以避免第二次检索该值,但我猜这不是重要的优化。

于 2014-10-17T23:31:23.040 回答
2

由于ExpandoObject实现IDictionary<string, Object>了事情可以变得更容易一些。我们还可以将返回类型更改为,void因为我们不需要重新分配结果。

void UpdatePrice(ExpandoObject expando, decimal price)
{
    var map = (IDictionary<string, Object>)expando;
    if (map.ContainsKey("price"))
        map["price"] = price;
    foreach (var value in map.Values) 
    {
        if (value is ExpandoObject)
            UpdatePrice((ExpandoObject)value, price);
    }
}
于 2014-10-17T23:42:00.703 回答
1

我只是对此进行了一些测试,并且能够通过使 expando 成为动态来使其工作:

    public static ExpandoObject DoWork(ExpandoObject obj)
    {
        dynamic expando = obj;

        //update price
        if (obj.Any(c => c.Key == "price"))
            expando.price = 354.11D;

        foreach (var item in expando)
        {
            if (item.Value is ExpandoObject)
            {
                //call recursively
                DoWork(item.Value);
            }
        }
        return expando;
    }

它限制了类型安全,但看起来你无论如何都没有那种奢侈,根据MSDN ,动态实际上是与 expandos 交互的最佳方式:

“在 C# 中,要为 ExpandoObject 类的实例启用后期绑定,您必须使用 dynamic 关键字。有关详细信息,请参阅使用类型动态(C# 编程指南)。”

这意味着如果您不使用dynamic关键字,您将在 CLR 而不是 DLR 中运行 Expando,这会产生一些奇怪的后果,例如无法设置值。希望这会有所帮助。

于 2014-10-17T23:35:07.153 回答