应对数组的索引属性



说我有一些json会出现在这样的数据包中:

{
    "LinkType1": "google",
    "LinkUrl1": "https://plus.google.com/test",
    "LinkShow1": 1,
    "LinkType2": "facebook",
    "LinkUrl2": "https://www.facebook.com/test",
    "LinkShow2": 0,
    "LinkType3": "linkedin",
    "LinkUrl3": "http://www.linkedin.com/test",
    "LinkShow3": 1,
    "count": 3,
    "errorCode": 0,
    "errorMessage": "Success"
}

请注意,一切如何恢复为同一属性,但是索引上有一个索引吗?

我很想能够对该数据进行估算,就像它是一个数组而不是单个属性一样。在下面的类中,将其列出的最佳方法是什么?我正在使用Newtonsoft JSON库进行序列化,因此使用的解决方案是首选的。

    public class LinksResult
    {
        public List<LinkData> Links { get; set; } 
        [JsonProperty("count")]
        public int Count { get; set; }
        [JsonProperty("errorCode")]
        public int ErrorCode { get; set; }
        [JsonProperty("errorMessage")]
        public string ErrorMessage { get; set; }
    }
    public class LinkData
    {
        public string LinkType { get; set; }
        public string LinkUrl { get; set; }
        public bool LinkShow { get; set; }
    }

您可以使用自定义JsonConverter将JSON数据验证为所需的结构。这是转换器的代码。

class LinksResultConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(LinksResult));
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = JObject.Load(reader);
        LinksResult result = new LinksResult();
        result.Count = (int)obj["count"];
        result.ErrorCode = (int)obj["errorCode"];
        result.ErrorMessage = (string)obj["errorMessage"];
        result.Links = new List<LinkData>();
        for (int i = 1; i <= result.Count; i++)
        {
            string index = i.ToString();
            LinkData link = new LinkData();
            link.LinkType = (string)obj["LinkType" + index];
            link.LinkUrl = (string)obj["LinkUrl" + index];
            link.LinkShow = (int)obj["LinkShow" + index] == 1;
            result.Links.Add(link);
        }
        return result;
    }
    public override bool CanWrite
    {
        get { return false; }
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

要使用转换器,只需在LinksResult类中添加[JsonConverter]属性,如下所示。(请注意,您不需要此方法的[JsonProperty]属性,因为JSON属性名称和实际类成员之间的映射直接由转换器处理。)

[JsonConverter(typeof(LinksResultConverter))]
public class LinksResult
{
    public List<LinkData> Links { get; set; }
    public int Count { get; set; }
    public int ErrorCode { get; set; }
    public string ErrorMessage { get; set; }
}

然后,您可以这样进行审理:

LinksResult result = JsonConvert.DeserializeObject<LinksResult>(json);

小提琴:https://dotnetfiddle.net/56b34h

布莱恩的回答非常好,这使我有80%的方式进入了我想要的地方。但是,如果这种模式发生在许多不同的对象上,那么一遍又一遍地使用并不是一个很好的实现。

我做了一些更通用的东西。"页面"具有的接口。

public interface IPage<TItem>
{
    int Count { get; set; }
    List<TItem> PageItems { get; set; }
}

然后页面转换器本身。

public class PageConverter<TPage, TItem> : JsonConverter
        where TPage : IPage<TItem>, new()
        where TItem : new()
{
    private readonly Regex _numberPostfixRegex = new Regex(@"d+$");
    public override bool CanWrite
    {
        get { return false; }
    }
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(TPage));
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var obj = serializer.Deserialize<JObject>(reader);
        var page = new TPage();
        serializer.Populate(obj.CreateReader(), page); //Loads everything that isn't a part of the items. 
        page.PageItems = new List<TItem>();
        for (int i = 1; i <= page.Count; i++)
        {
            string index = i.ToString();
            //Find all properties that have a number at the end, then any of those that are the same number as the current index.
            //Put those in a new JObject.
            var jsonItem = new JObject();
            foreach (var prop in obj.Properties().Where(p => _numberPostfixRegex.Match(p.Name).Value == index))
            {
                jsonItem[_numberPostfixRegex.Replace(prop.Name, "")] = prop.Value;
            }
            //Deserialize and add to the list.
            TItem item = jsonItem.ToObject<TItem>(serializer);
            page.PageItems.Add(item);
        }
        return page;
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

因此,所需的只是在链接结果上实现它:

[JsonConverter(typeof(PageConverter<LinksResult, LinkData>))]
public class LinksResult : IPage<LinkData>
{
    public int Count { get; set; }
    public List<LinkData> PageItems { get; set; }
}

我发现您可以通过jsonserializersettings来控制大写的序列化,因此最好将细节留给所选的序列化器,而不是我的转换器。

小提琴:https://dotnetfiddle.net/7khwyy

这是您可以应用的类似解决方案。请参阅带有所有字典属性的对象序列化的json。

的答案。

下面是我提出的解决方案。

有一些样本JSON与以下相似。JSON非常平坦,您可以看到索引的检查/传输属性。我想让纽顿斯托夫(Newtonsoft)尽可能多地进行繁重的举重。

{
  "id": "209348",
  "Check__00__amount": 10000,
  "Check__00__payableTo": "ABC Company",
  "Check__00__receivedFrom": "Mike",
  "Check__01__amount": 20000,
  "Check__01__payableTo": "XYZ Company",
  "Check__01__receivedFrom": "Jim",
  "Transfer00__Amount": 50000.0,
  "Transfer00__CompanyTransferringFrom": "DEF Company",
  "Transfer00__Type": "Partial",
  "Transfer01__Amount": 55000.0,
  "Transfer01__CompanyTransferringFrom": "GHI Company",
  "Transfer01__Type": "Full"
}

供应到的类型。最高对象事务,具有两个列表属性,用于检查和转移。我正在使用自定义转换器和自定义属性。

[JsonConverter(typeof(TestConverter))]
public class Transaction
{
    [JsonProperty("id")]
    public int Id { get; set; } = default!;
    
    [RegexIndexedPropertiesToList(@"Transferd+__")]
    public List<Transfer> Transfers { get; set; } = default!;
    
    [RegexIndexedPropertiesToList(@"Check__d+__")]
    public List<Check> Checks { get; set; } = default!;
}
public class Check
{
    
    [JsonProperty("amount")]
    public decimal Amount { get; set; }
    [JsonProperty("payableTo")]
    public string PayableTo { get; set; } = default!;
    [JsonProperty("receivedFrom")]
    public string ReceivedFrom { get; set; } = default!;
    
}
public class Transfer
{
    [JsonProperty("Amount")]
    public decimal Amount { get; set; }
    [JsonProperty("CompanyTransferringFrom")]
    public string CompanyTransferringFrom { get; set; } = default!;
    [JsonProperty("Type")]
    public string Type { get; set; } = default!;
}

自定义属性。允许设置正则。此正则应该能够匹配要将其变成列表的属性。您可以在交易类中的支票/列表属性上看到它。

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class RegexIndexedPropertiesToListAttribute : Attribute
{
    public string Regex { get; }
    public RegexIndexedPropertiesToListAttribute(string regex)
    {
        Regex = regex;
    }
}

转换器

public class TestConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
    {
        var obj = JObject.Load(reader);
            
        //get a new instance and populate it to prevent infinite recursion on the converter, this will allow default serializer behavior to work
        var returnable = Activator.CreateInstance(objectType) ?? throw new InvalidOperationException("could not create instance");
        JsonConvert.PopulateObject(obj.ToString(), returnable); //use regular deserialization
        IndexedPropertiesToList(returnable, obj);
        return returnable;
    }
    
    private static void IndexedPropertiesToList(object returnable, JObject obj)
    {
        //get all the index to list properties
        var propsForConversion = returnable.GetType().GetProperties()
            .Where(x => x.IsDefined(typeof(RegexIndexedPropertiesToListAttribute), false));
        foreach (var prop in propsForConversion)
        {
            var attribute = (prop.GetCustomAttributes(typeof(RegexIndexedPropertiesToListAttribute), false).FirstOrDefault() as RegexIndexedPropertiesToListAttribute)
                            ?? throw new Exception("attribute not found");
            //assume the prperty to be set is a list
            var list = Activator.CreateInstance(prop.PropertyType) as IList ??
                       throw new InvalidOperationException("could not create instance");
            
            var regex = new Regex(attribute.Regex);
            //get the properties that match the regex and then group them using the regex as a prefix
            var matchedProperties = obj.Properties().Where(x => regex.IsMatch(x.Path)).ToList();
            var groups = matchedProperties.GroupBy(x => regex.Match(x.Path).Value).ToList();
            foreach (var group in groups)
            {
                var newObj = new JObject(); //create a new jobject will use this to deserialize the properties into type so that normal deserialization works
                foreach (var property in group)
                {
                    var name = property.Name.Replace(group.Key, "");
                    newObj.Add(name, property.Value); //add the property to the new object with no index
                }
                //assumes the List is of a generic type
                var genericType = prop.PropertyType.GenericTypeArguments[0];
                
                var instance = newObj.ToObject(genericType) ??
                               throw new InvalidOperationException("could not deserialize");
                list.Add(instance);
            }
            //set the constructed list of deserialized objects to the property
            prop.SetValue(returnable, list);
        }
    }
    public override bool CanConvert(Type objectType)
    {
        throw new NotImplementedException();
    }
}

首先,在这种情况下,转换器填充了最高级别。然后,它寻找用属性装饰的属性。它使用该正则表达式匹配JSON的字段,然后通过比赛部分进行组,Check__00__和Check__01__将是两个分组,每个分组为相应的属性,每个分组分别为3个JTOKENS。从那里开始,它可以通过分组字段构建一个新的职位。该局限将删除该名称的正则匹配部分,该部分允许使用JSONPROPERTY属性进行映射。然后,它使用默认的newtonsoft quesialization获取列表类型的新实例并将其添加到列表中,然后在顶级实例上设置列表属性。

它适合我需要的东西。

使用常规的纽顿斯托夫·德莱齐齐(Deserialzation)仅需要自定义转换器和属性似乎在一些相当大的有效载荷上执行很快。

我上面认为的是,它正在用列表进行开发,如果事实并非如此,则需要进一步改编。它将无法处理更深的筑巢,尽管我认为如果需要的话可以添加它,并且可能在原始列表中表现不佳,尽管我认为这也可以用一些其他代码来处理。<<<<<<<<<<<<<<

相关内容

  • 没有找到相关文章

最新更新