访问json文件中的子字段

时间:2015-05-22 16:21:37

标签: c# .net json.net

假设我有一个具有以下结构的JSON文件。我如何访问元数据字段中的属性名称。

{
  "mappings": {
    "basedoc_12kja": {
      "properties": {
        "created": {
          "type": "date",
          "format": "dateOptionalTime"
        },
        "customerID": {
          "type": "string"
        },
        "deleted": {
          "type": "boolean"
        },
        "documentID": {
          "type": "string"
        },
        "id": {
          "type": "string"
        },
        "metadata": {
          "properties": {
            "Cert": {
              "type": "string"
            },
            "Exp_date": {
              "format": "dateOptionalTime"
            },
          }
        }
      }
    }
  }
}

Mappings是一个文档数组,映射的每个子字段都有不同的代码。我想获取每个文档的元数据字段,以找出它们之间共有的元数据字段。

我无法实例化此文档。

var response = esReader.GetIndicesMapping();

foreach (var mapping in response.Response.Values)
{
   // Parse JSON into dynamic object, convenient!
   dynamic results = JObject.Parse(mapping);

   List<DocumentType> deserializedObject = JsonConvert.DeserializeObject<List<DocumentType>>(mapping);

}  

异常

  

{“无法将当前JSON对象(例如{\”name \“:\”value \“})反序列化为类型'System.Collections.Generic.List`1 [DocumentType]',因为该类型需要JSON数组(例如[1,2,3])要正确反序列化。\ r \ n要修复此错误要么将JSON更改为JSON数组(例如[1,2,3]),要么更改反序列化类型以使其正常.NET类型(例如,不是整数的基本类型,不是类似于数组或List的集合类型),可以从JSON对象反序列化.JsonObjectAttribute也可以添加到类型中以强制它从JSON对象反序列化。 r \ nPath'mappings',第2行,第14位。“}

希望的结果是获取CertExp_date字段的名称

修改

public class DocumentType
    {

        public string Id { set { DocumentID = value; } get { return DocumentID; } }

        public string DocumentID { set; get; }       

        public DateTime Created { set; get; }
         .
         .
         .
        public Dictionary<string, object> Metadata { set; get; }


    }

2 个答案:

答案 0 :(得分:3)

这里的问题是您的数据结构与JSON不匹配:

  1. JSON中没有数组。因此,您无法将其反序列化为C#List。
  2. &#34; DocumentType&#34; class根本不匹配JSON。该类具有Created,CustomerID和Deleted属性,这些属性类似于DateTime和string。但JSON并没有将它们作为DateTime或字符串。它们是具有名为&#34; type&#34;的子属性的对象。和&#34;格式。&#34;该属性&#34;元数据&#34;不是字典:它是一个具有单个属性的对象,名为&#34; properties&#34;这应该是一本字典。
  3. 案件不匹配。
  4. 不要用Id和DocumentId做那个奇怪的事情。该类应完全匹配JSON和字面。没有业务逻辑隐藏在属性中。
  5. 根对象有一个名为&#34; mappings&#34;的属性。所以在你拿到文件之前你需要深入了解。
  6. 成功获取文档后,您需要深入查看名为&#34; properties&#34;到达你感兴趣的领域。
  7. 我怀疑可能有多个文件,而且#34;映射&#34; property包含这些文档的列表,其中属性名称是动态的,并且对应于文档的名称。处理它是完全合理的,但不使用反序列化+列表方法。

    我在这里看到3种方法:

    1. 修复JSON。不确定你的情况是否可行。如果是这样,首先使映射保持数组,而不是让每个文档都是文档名称命名的属性。
    2. 修复反序列化代码以匹配JSON文档。 json2csharp做得很好,所以从这开始。它只是不知道&#34;映射&#34;它实际上是一个字典,而不仅仅是一个名为&#34; basedoc12_kja的属性。&#34;
    3. 根本不要对它进行反序列化。只需查询元数据。看看http://www.newtonsoft.com/json/help/html/QueryingLINQtoJSON.htm,它显示了使用JObject属性和LINQ查询JSON的几种方法。
    4. 选项1

      如果您走这条路线,稍微清理一下JSON的示例:

      {
        "mappings": [
          {
              "name"" : "basedoc_12kja",
              "properties": {
                ""created": "20150522",
                etc.
         },
      

      注意&#34;映射&#34;是一个数组,名称成为文档的属性。现在你可以制作一个List&lt;&gt;或使用JArray。更好的是摆脱顶部未使用的东西,如:

      [
         {
            "name" : "basedoc_12kja",
            "properties": {
               "created"": "20150522",
               etc.
         },
      ]
      

      现在它只是一个没有&#34;映射的数组&#34;一点都不。

      **选项2 ** 这是通过反序列化完成此操作的代码。有两个部分。第一步是使用json2charp生成的内容。我将此处包含在此供参考:

      public class Created
      {
          public string type { get; set; }
          public string format { get; set; }
      }
      
      public class CustomerID
      {
          public string type { get; set; }
      }
      
      public class Deleted
      {
          public string type { get; set; }
      }
      
      public class DocumentID
      {
          public string type { get; set; }
      }
      
      public class Id
      {
          public string type { get; set; }
      }
      
      public class Cert
      {
          public string type { get; set; }
      }
      
      public class ExpDate
      {
          public string format { get; set; }
      }
      
      public class Properties2
      {
          public Cert Cert { get; set; }
          public ExpDate Exp_date { get; set; }
      }
      
      public class Metadata
      {
          public Properties2 properties { get; set; }
      }
      
      public class Properties
      {
          public Created created { get; set; }
          public CustomerID customerID { get; set; }
          public Deleted deleted { get; set; }
          public DocumentID documentID { get; set; }
          public Id id { get; set; }
          public Metadata metadata { get; set; }
      }
      
      public class Basedoc12kja
      {
          public Properties properties { get; set; }
      }
      
      public class Mappings
      {
          public Basedoc12kja basedoc_12kja { get; set; }
      }
      
      public class RootObject
      {
          public Mappings mappings { get; set; }
      }
      

      然后,将Basedoc12kja重命名为DocumentType,并更改RootObject以保存字典。你明白了:

      public class DocumentType
      {
          public Properties properties { get; set; }
      }
      
      public class RootObject
      {
          public Dictionary<string, DocumentType> mappings { get; set; }
      }
      

      如果您想访问除Cert和Exp_date之外的其他属性,请将元数据更改为:

      public class Metadata
      {
          public Dictionary<string,object> properties { get; set; }
      }
      

      现在可以反序列化您的文档:

      JObject results = JObject.Parse(mapping);
      RootObject ro = results.ToObject<RootObject>()
      

      您可以通过映射枚举并获取属性。由于JSON结构,它们仍然很混乱,但你至少可以到达那里。

      我希望这有帮助!

答案 1 :(得分:2)

这里有一个命名属性的分层字典,其中每个属性都可以有一个类型,格式,以及可能的命名子属性的嵌套字典 - metadata。您可以使用以下数据模型表示:

[DataContract]
public class PropertyData
{
    [DataMember(Name="type", EmitDefaultValue=false)]
    public string Type { get; set; }
    [DataMember(Name = "format", EmitDefaultValue = false)]
    public string Format { get; set; }

    [DataMember(Name = "properties", EmitDefaultValue = false)]
    public Dictionary<string, PropertyData> Properties { get; set; }
}

[DataContract]
public class Mappings
{
    [DataMember(Name = "mappings", EmitDefaultValue = false)]
    public Dictionary<string, PropertyData> DocumentMappings { get; set; }
}

(这个数据模型没有捕获这样一个事实,即给定的属性(可能)只能是一个简单类型或具有嵌套属性的复杂类型 - 但不能同时兼顾两者。但这似乎足以满足您的需求。 )

然后,根据上面的JSON,您将读入它并将其转换为文档名称到元数据属性名称的字典,如下所示:

        var mappings = JsonConvert.DeserializeObject<Mappings>(json);
        Debug.WriteLine(JsonConvert.SerializeObject(mappings, Formatting.Indented));  // Verify that all was read in.
        var metadataNames = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.Properties["metadata"].Properties.Select(p => p.Key).ToList());
        Debug.WriteLine(JsonConvert.SerializeObject(metadataNames, Formatting.Indented)); // Inspect the resulting mapping table.

结果是您想要的元数据名称字典:

{
  "basedoc_12kja": [
    "Cert",
    "Exp_date"
  ]
}

如果您担心有时可能会遗漏嵌套的metadata,因此在上面的查询中生成NullReferenceExceptions,您可以按如下方式添加空检查:

// Extension methods to query or walk through nested properties, avoiding null reference exceptions when properties are missing
public static class PropertyDataExtensions
{
    public static IEnumerable<KeyValuePair<string, PropertyData>> GetProperties(this PropertyData data)
    {
        if (data == null || data.Properties == null)
            return Enumerable.Empty<KeyValuePair<string, PropertyData>>();
        return data.Properties;
    }

    public static PropertyData GetProperty(this PropertyData data, string name)
    {
        if (data == null || data.Properties == null)
            return null;
        PropertyData child;
        if (!data.Properties.TryGetValue(name, out child))
            return null;
        return child;
    }
}

然后:

        var metadataNamesSafe = mappings.DocumentMappings.ToDictionary(doc => doc.Key, doc => doc.Value.GetProperty("metadata").GetProperties().Select(p => p.Key).ToList());