根据索引返回一些数组元素

时间:2018-01-04 13:29:09

标签: c#

我有一个奇怪的JSON,我需要反序列化。
它看起来像这样:

{
    "Properties": [
        { "A": "aaa", "B": "bbb", "C": "ccc" },
        { "X": "xxx", "Y": "yyy" },
        { "many other": "items" }
    ],
    "Products": [
        { "Id": 0, "PropertiesIndexes": [ 0, 1 ] },
        { "Id": 1, "PropertiesIndexes": [ 0, 1, 2 ] }
    ]
}

properties数组可以包含任意数量和名称的键,每个产品都通过索引访问属性数组。

我们需要将这些JSON文件存储在MongoDB中,因为它们可能非常庞大(我说的是一个文件的几百Mb)我需要拆分它们(Mongo有16Mb的限制)。因此,每个属性都是Mongo文档,每个产品都是Mongo文档 因为我不能按插入顺序获取属性,所以我们决定保存每个产品的所有属性。

以下是用于反序列化此数据的类(使用JSON.Net):

public class Entity {
    public OrderedDictionary Properties { get; set; }
    public IEnumerable<Product> Products { get; set; }
}

public class Product {
    [JsonIgnore]
    public Entity Entity { get; set; }

    publid int Id { get; set; }
    public int[] PropertiesIndexes { get; set; }
}

现在,我需要直接从产品中访问实际的属性数据,这样(假设的)生成的JSON就像这样:

{
    "Products": [
        { "Id": 0,
          "PropertiesData": [
                { "A": "aaa", "B": "bbb", "C": "ccc" },
                { "X": "xxx", "Y": "yyy" }
            ]
        },
        { "Id": 1,
          "PropertiesData": [
                { "A": "aaa", "B": "bbb", "C": "ccc" },
                { "X": "xxx", "Y": "yyy" },
                { "many other": "items" }
            ]
        }
    ]
}

我天真的实现是这样的:

// in Product
[JsonIgnore]
public IDictionary<string, object> PropertiesData {
    get {
        if (this.Entity != null && this.Entity.Properties != null) {
            var data = new Dictionary<string, object>();
            for (int i = 0; i < this.PropertiesIndexes.Length; i++) {
                data.Add(
                    this.Entity.Properties.Cast<DictionaryEntry>().ElementAt(this.PropertiesIndexes[i]).Key.ToString(),
                    this.Entity.Properties[this.PropertiesIndexes[i]]);
            }

            return data;
        }

        return new Dictionary<string, object>();
    }
}

但它很慢(正如我所说,我有大量数据)并且非常难看。

当我们使用ExpandoObject时,我有一个很好的,快速且内存有效的yeilding方法:

private IEnumerable<ExpandoObject> getPropertiesDataFromEntity() {
    for (int i = 0; i < this.PropertiesIndexes.Count; i++) {
        yield return this.Entity.Properties[this.PropertiesIndexes[i]];
    }
}

ExpandoObject在我们的应用程序环境中有自己的问题:它们并不总是在Mongo中正确保存,并且在没有自定义序列化程序的情况下它们不会被WCF序列化。

1 个答案:

答案 0 :(得分:1)

我已经编写了一些非常简单的东西,从源JSON到目标JSON 它包括4个非常简单的类。

前两个设计用于从源JSON反序列化:

public class Source
{
    public Dictionary<string, string>[] Properties { get; set; }

    public SourceProduct[] Products { get; set; }

}

public class SourceProduct
{
    public int Id { get; set; }

    public int[] PropertiesIndexes { get; set; }
}

第三类是目标产品:

public class Product
{
    private List<Dictionary<string, string>> _propertiesData = new List<Dictionary<string, string>>();

    public int Id { get; set; }

    public List<Dictionary<string, string>> PropertiesData { get { return _propertiesData; } }
}

最后一个类是最终目标,我将数据从源转换为目标:

public class Target
{
    private List<Product> _products = new List<Product>();

    public IEnumerable<Product> Products { get { return _products; } }

    public Target(Source source)
    {
        foreach(var sourceProduct in source.Products)
        {
            var product = new Product()
            {
                Id = sourceProduct.Id

            };
            foreach(var propertyIndex in sourceProduct.PropertiesIndexes)
            {
                product.PropertiesData.Add(source.Properties[propertyIndex]);
            }
            _products.Add(product);
        }
    }
}

使用这些类,您的客户端代码就变成了这样:

var targetJson = JsonConvert.SerializeObject
    (
        new Target(JsonConvert.DeserializeObject<Source>(sourceJson))
    );