JSON JavaScriptSerializer 需要嵌套数组而不命名元素

JSON need nested array for JavaScriptSerializer without naming the element

我想为此使用 JavaScriptSerializer,因为我担心很多东西可能会损坏,而且我无法在客户端更改任何东西。

如果 Json.Net 是最好的方法,那么我会尝试,但我需要一个例子。

我有这个class

Class定义

[DataContract] 
[Serializable] 
public class Family 
{
    [DataMember(Order = 0)]
    public List<Member> members { get; set; }
}

[DataContract] 
[Serializable] 
public class Member 
{
    [DataMember(Order = 0)]    
    public string FName { get; set; }
    [DataMember(Order = 1)]
    public string LName { get; set; }
    [DataMember(Order = 2)]
    public string DOB { get; set; }
    [DataMember(Order = 3)]
    public string Gender { get; set; }
    [DataMember(Order = 4)]
    public string Type { get; set; } 
}

我正在反序列化的 JSON 看起来像这样

JSON 例子

[
    {
        "Family": [
            {
                "FName": "Jane",
                "LName": "Prospect",
                "DOB": "04/01/1980",    
                "Gender": "Female",
                "Type": "Adult"
            },
            {...}
        ]
    },
    { 
        "OptionChoice": 34,
        "OptionText": "Aquatics"
    },
    {...},
    {...}
]

我可以很好地反序列化 Answer 对象(OptionChoice、OptionText)。 然而,答案对象有一个充满空值的附加项,它正在解析 JSON 的 Family 部分。我真的不想那样。

当我尝试反序列化 Family 部分时出现错误

Type 'Family' is not supported for deserialization of an array.

它说 Family.members 有一个空值。它在寻找 "Family": [ "members": {...},{...}] ?

如何在不更改 JSON 示例的情况下使它正常工作?


dbc 回答后更新:

这是我的模型

[DataContract]
[Serializable]
public class Answer
{
    [DataMember(Order = 2, EmitDefaultValue = false)]
    public int FormID { get; set; }

    [DataMember(Order = 3,EmitDefaultValue = false)]
    public int Question { get; set; }

    [DataMember(Order = 5)]
    public int OptionChoice { get; set; }

    [DataMember(Order = 6,IsRequired = false)]
    public string OptionText { get; set; }

    [DataMember(Order = 5, EmitDefaultValue = false)]
    public bool lockAnswer { get; set; }

    [DataMember(Order = 1,EmitDefaultValue= false)]
    public List<FamilyMember> Family { get; set; }
}

[DataContract]
[Serializable]
public class FamilyMember
{
    [DataMember(Order = 0)]
    public string FName { get; set; }
    [DataMember(Order = 1)]
    public string LName { get; set; }
    [DataMember(Order = 2)]
    public string DOB { get; set; }
    [DataMember(Order = 3)]
    public string Gender { get; set; }
    [DataMember(Order = 4)]
    public string Type { get; set; }
}

在我创建 SO 更新的这一点上,我用 Rubber Duck Debugging

解决了这个问题

原来我有一些正则表达式正在剥离所有“[”和“]”,然后手动将它们添加回去,但只在末端。

http://pro.jsonlint.com/ 上的这个很酷的比较工具帮了大忙。

谢谢!如果将来有人读到这篇文章并想在 JSON.net 中展示一种可测试的方法(我试过但卡住了),请这样做。

您的问题包含两个相关问题:

  1. 加载 Family 列表时出现异常。

    这里的问题是,正如你所怀疑的,没有members对应的属性。你的 JSON 有一个对象数组,每个对象可能有一个数组值 属性 Family。因此,您的数据模型应如下所示:

    public class ResponseItem
    {
        public int? OptionChoice { get; set; }
        public string OptionText { get; set; }
        public List<FamilyMember> Family { get; set; }
        // Other fields not shown from {...}
    }
    
    public class FamilyMember
    {
        public string FName { get; set; }
        public string LName { get; set; }
        public string DOB { get; set; }
        public string Gender { get; set; }
        public string Type { get; set; }
    }
    
  2. 你说,"Hower the answer object has an additional item full of nulls where it is parsing the Family section of the JSON. I don't really want that." 这可以通过将 JSON 反序列化为多态数组来完成,其中每个可能的派生类型都只有最少数量的字段。但是,由于 JSON 中没有 __type information,您需要添加一些稍微繁琐的逻辑来为每个数组元素选择正确的具体类型。您的数据模型如下所示:

    public interface IResponseItem // base interface for all possible responses
    {
    }
    
    public class FamilyResponse : IResponseItem
    {
        public List<FamilyMember> Family { get; set; }
    }
    
    public class OptionsResponse : IResponseItem
    {
        public int OptionChoice { get; set; }
        public string OptionText { get; set; }
    }
    

    在像这样的复杂序列化情况下,人们似乎更喜欢 Json.NET,但根据您的问题,JavaScriptSerializer 仍然是可能的。您必须通过匹配 属性 名称从基本 IResponseItem 类型编码 JavaScriptConverter 到 select 适当的派生类型,例如:

    public class PolymorphicTypeConverter : JavaScriptConverter
    {
        public Type BaseType { get; private set; }
    
        public Type[] DerivedTypes { get; private set; }
    
        public PolymorphicTypeConverter(Type baseType, IEnumerable<Type> derivedTypes)
        {
            this.BaseType = baseType;
            this.DerivedTypes = derivedTypes.ToArray();
        }
    
        static MemberInfo FindMember(Type type, string name)
        {
            try
            {
                var propInfo = type.GetProperty(name,
                    BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
                if (propInfo != null
                    && propInfo.GetSetMethod() != null
                    && propInfo.GetIndexParameters().Length == 0)
                    return propInfo;
                var fieldInfo = type.GetField(name,
                    BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Public);
                if (fieldInfo != null)
                    return fieldInfo;
            }
            catch (AmbiguousMatchException)
            {
                return null;
            }
            return null;
        }
    
        IEnumerable<Type> AncestorsAndSelf(Type type)
        {
            for (; type != null; type = type.BaseType)
                if (DerivedTypes.Contains(type))
                    yield return type;
        }
    
        Type FindUniqueTypeMatch(IDictionary<string, object> jsonProperties)
        {
            List<Type> matches = new List<Type>();
    
            foreach (var type in DerivedTypes)
            {
                if (type.IsInterface)
                    continue; // Bug?
                bool isMatch = true;
                foreach (var name in jsonProperties.Keys)
                {
                    if (FindMember(type, name) == null)
                    {
                        isMatch = false;
                        break;
                    }
                }
                if (isMatch)
                {
                    matches.Add(type);
                }
            }
            if (matches.Count == 0)
                return null;
            else if (matches.Count == 1)
                return matches[0];
            else
            {
                // Multiple matches.
                // If there is a common base type to all matches, return it.  Otherwise, give up.
                var candidates = AncestorsAndSelf(matches[0]).Reverse();
                foreach (var match in matches.Skip(1))
                {
                    candidates = candidates.Zip(AncestorsAndSelf(match).Reverse(), (t1, t2) => (t1 == t2 ? t1 : null)).Where(t => t != null);
                }
                return candidates.LastOrDefault();
            }
        }
    
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            var subtype = FindUniqueTypeMatch(dictionary);
            if (subtype == null)
                throw new JsonSerializationException();
            var method = serializer.GetType().GetMethod("ConvertToType");
            var generic = method.MakeGenericMethod(subtype);
            return generic.Invoke(serializer, new object [] { dictionary } );
        }
    
        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            // Should never be called.
            throw new NotImplementedException();
        }
    
        public override IEnumerable<Type> SupportedTypes
        {
            get
            {
                return new Type[] { BaseType };
            }
        }
    }
    

    仅当 JSON 数组中的每个对象都具有与派生类型数组中的一种且仅一种类型匹配的属性时才有效。如果不能保证这一点,例如,因为 null 字段未序列化导致多个匹配,您将需要增强转换器以进行最佳猜测匹配。

    然后这样称呼它:

        var serializer = new JavaScriptSerializer();
        serializer.RegisterConverters(new JavaScriptConverter[] { new PolymorphicTypeConverter(typeof(IResponseItem), new Type[] { typeof(FamilyResponse), typeof(OptionsResponse) }) });
        var responseArray = serializer.Deserialize<IResponseItem[]>(json);