复杂JSON对象的C#数据协定

时间:2016-09-27 17:35:50

标签: c# json datacontract

这可能是非常简单的事情,我到处寻找并尝试了我能想到的一切。所以我很抱歉,如果这是一个简单的搜索,我只是在寻找错误的东西。我也是数据合同和JSON的新手,所以这可能并不复杂。

我正在创建一个API来摄取JSON并将其存储在我们的数据库中。 JSON看起来像这样:

{
"appname" : "MyApp",
"key" : "Test123",
"data" :[
  { "data1" : "10551296", "data2" : "TrainingIns", "data3" : "Completed"}
  ,
  { "connectorType" : "webserver-to-appserver", "sourceUri" : "data4", "destinationUri" : "data5", "rails" : "N", "data6" : "null" }
  ,
  { "groupId" : "group1", "failbackAction" : "null", "normal" : "null", "failoverAction" : "null", "failbackAction" : "null", "failoverAction" : "null", "artifactId" : "mywebserver", "normalState" : "null" }
  ,
  { "instanceId" : "10551296abc" }]
,
"updateId" : "MyID",
"updateTS" : "30-AUG-16 05.56.24.000000000 AM" ,
"creationUser" : "APICall"
}

'数据' field将是一个包含可变数量的JSON对象的数组。我遇到的问题源于要么没有在数据中获取数据'对象或完全未定义。

[DataContract]
public class Update_DB
{
    [DataMember(Name = "appname", IsRequired = true)]
    public string appname { get; set; }
    [DataMember]
    public string key { get; set; }

    [DataMember(Name="data",IsRequired = true)]
    public List<JsonValue> data { get; set; }

    [DataMember]
    public string updateId { get; set; }
    [DataMember]
    public string updateTS { get; set; }
    [DataMember]
    public string creationUser { get; set; }
}

我已经聚集了我可能需要某种收藏品吗?我已经尝试了我能找到的所有内容,但我不知道应该如何定义数据成员以获取数据&#39;。 当我这样做时,上面的合同给了我空数组:

string x = JsonConvert.SerializeObject(collection.data);

我可以获得所有其他字段,我只需要转换数据&#39;字段变成字符串。

希望这是足够的信息。在此先感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

在正常情况下,您可以将data属性定义为List<Dictionary<string, string>>,如下所示:

    [DataMember(Name = "data", IsRequired = true)]
    public List<Dictionary<string, string>> data { get; set; }

然后,您将能够使用Json.NET成功序列化和反序列化它。不幸的是,您的一个数据对象具有重复密钥

  {
     "groupId":"group1",
     "failbackAction":"null",
     "normal":"null",
     "failoverAction":"null",
     "failbackAction":"null",
     "failoverAction":"null",
     "artifactId":"mywebserver",
     "normalState":"null"
  },

JSON standard不建议使用重复的密钥,其中指出:

  

当对象中的名称不唯一时,接收此类对象的软件的行为是不可预测的。

此外,c#词典当然不支持重复键,数据契约序列化不会重复属性名称。

但是,可以使用Json.NET&#39; JsonReader读取带有重复键的JSON对象,并创建custom JsonConverter来处理重复的键。

首先,定义以下类来替换JsonValueJsonValue是一个特定于silverlight的类,其使用范围为deprecated in overall .Net

[JsonConverter(typeof(JsonValueListConverter))]
public sealed class JsonValueList
{
    public JsonValueList()
    {
        this.Values = new List<KeyValuePair<string, string>>();
    }

    public List<KeyValuePair<string, string>> Values { get; private set; }
}

class JsonValueListConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(JsonValueList).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var jsonValue = (existingValue as JsonValueList ?? new JsonValueList());
        if (reader.TokenType != JsonToken.StartObject)
            throw new JsonSerializationException("Invalid reader.TokenType " + reader.TokenType);
        while (reader.Read())
        {
            switch (reader.TokenType)
            {
                case JsonToken.Comment:
                    break;
                case JsonToken.PropertyName:
                    {
                        var key = reader.Value.ToString();
                        if (!reader.Read())
                            throw new JsonSerializationException(string.Format("Missing value at path: {0}", reader.Path));
                        var value = serializer.Deserialize<string>(reader);
                        jsonValue.Values.Add(new KeyValuePair<string, string>(key, value));
                    }
                    break;
                case JsonToken.EndObject:
                    return jsonValue;
                default:
                    throw new JsonSerializationException(string.Format("Unknown token {0} at path: {1} ", reader.TokenType, reader.Path));
            }
        }
        throw new JsonSerializationException(string.Format("Unclosed object at path: {0}", reader.Path));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var jsonValue = (JsonValueList)value;
        writer.WriteStartObject();
        foreach (var pair in jsonValue.Values)
        {
            writer.WritePropertyName(pair.Key);
            writer.WriteValue(pair.Value);
        }
        writer.WriteEndObject();
    }
}

请注意[JsonConverter(typeof(JsonValueListConverter))]的使用。这指定在序列化和反序列化JsonValueList时使用自定义转换器。

接下来,按如下方式定义您的Update_DB课程:

[DataContract]
public class Update_DB
{
    [DataMember(Name = "appname", IsRequired = true)]
    public string appname { get; set; }
    [DataMember]
    public string key { get; set; }

    [DataMember(Name = "data", IsRequired = true)]
    public List<JsonValueList> data { get; set; }

    [DataMember]
    public string updateId { get; set; }
    [DataMember]
    public string updateTS { get; set; }
    [DataMember]
    public string creationUser { get; set; }
}

现在,您将能够成功序列化和反序列化您的JSON。样本fiddle

<强>更新

如果您没有重复的密钥,可以按如下方式定义您的类:

[DataContract]
public class Update_DB
{
    [DataMember(Name = "appname", IsRequired = true)]
    public string appname { get; set; }
    [DataMember]
    public string key { get; set; }

    [DataMember(Name = "data", IsRequired = true)]
    public List<Dictionary<string, string>> data { get; set; }

    [DataMember]
    public string updateId { get; set; }
    [DataMember]
    public string updateTS { get; set; }
    [DataMember]
    public string creationUser { get; set; }
}

然后是以下内容:

var collection = new Update_DB
{
    data = new List<Dictionary<string, string>>
    {
        new Dictionary<string, string>
        {
            {"data1", "10551296"},
            {"data2", "TrainingIns"},
            {"data3", "Completed"},
        },
        new Dictionary<string, string>
        {
            {"connectorType", "webserver-to-appserver"},
            {"sourceUri", "data4"},
            {"destinationUri", "data5"},
        },
    },
};

string x = JsonConvert.SerializeObject(collection.data, Formatting.Indented);

Console.WriteLine(x);

产生输出:

[
  {
    "data1": "10551296",
    "data2": "TrainingIns",
    "data3": "Completed"
  },
  {
    "connectorType": "webserver-to-appserver",
    "sourceUri": "data4",
    "destinationUri": "data5"
  }
]

示例fiddle

答案 1 :(得分:0)

另一种选择是使用动态关键字。您可以使用此类型的列表作为数据(如下所示)。

[DataContract]
public class Update_DB
{
    [DataMember(Name = "appname", IsRequired = true)]
    public string appname { get; set; }
    [DataMember]
    public string key { get; set; }

    [DataMember(Name = "data", IsRequired = true)]
    public List<dynamic> data { get; set; }

    [DataMember]
    public string updateId { get; set; }
    [DataMember]
    public string updateTS { get; set; }
    [DataMember]
    public string creationUser { get; set; }
}

从那里,您可以通过使用JSON.Net反序列化来使用该对象,并访问动态数据对象(假设您了解此动态对象的形状)。下面的内容将根据原始帖子中的输入字符串工作。

Update_DB dataObj = JsonConvert.DeserializeObject<Update_DB>(objectJSON);
string test = dataObj.data[1].connectorType; //evaluates to "webserver-to-appserver"

答案 2 :(得分:-1)

使用Json2CSharp.com确保一切正确无误:

<div class="slides">
  <div class="slide active" id="one">one</div>
  <div class="slide" id="two">two</div>
  <div class="slide" id="third">third</div>
  <div class="slide" id="fourth">fourth</div>
</div>

<button class="next" onclick="slide(this.className)">
  Next slide
</button>

<button class="prev" onclick="slide(this.className)">
  Previous slide
</button>
<script>
  var active = document.getElementsByClassName("active");

  function slide(prevNext) {
    if (prevNext === "next") {
      if (active[0].nextElementSibling) {
        active[0].nextElementSibling.className = "active";
        active[0].className = "";
      }
    } else {
      if (active[0].previousElementSibling) {
        active[0].previousElementSibling.className = "active";
        active[active.length - 1].className = "";
      }
    }
  }
</script>