Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 实现JsonConverter后,嵌套节点的格式不正确_C#_Json_Json.net - Fatal编程技术网

C# 实现JsonConverter后,嵌套节点的格式不正确

C# 实现JsonConverter后,嵌套节点的格式不正确,c#,json,json.net,C#,Json,Json.net,我有一个要序列化为JSON的对象列表,如下所示,因为这个列表中有一个复杂类型。我想将这个复杂类型更改为键/值对,其中每个键都是类型中属性的名称,每个值都是该属性的对应值。我尝试过多种解决方案,但没有一种对我有效 这是对象结构 public class Metadata { public string FirstName { get; set; } public string LastName { get; set; } } public class Data { //

我有一个要序列化为JSON的对象列表,如下所示,因为这个列表中有一个复杂类型。我想将这个复杂类型更改为
键/值
对,其中每个
都是类型中属性的名称,每个
都是该属性的对应值。我尝试过多种解决方案,但没有一种对我有效

这是对象结构

public class Metadata
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class Data
{
    // If I change the type of Metadata to IList<IDictionary<string, object>>
    // custom converter won't catch it at all when I pass it to its constructor
    //public IList<IDictionary<string, object>> Metadata { get; set; }
    public IList<Metadata> Metadata { get; set; }
    public int Length { get; set; }
    public string Type { get; set; }
}
我知道
JsonSerializer
本身不会更改对象的面,因此我尝试通过实现自定义
JsonConverter
来更改它:

public class KeyValue
{
    public string Key { get; set; }
    public string Value { get; set; }
}

class CustomMetadataConverter : JsonConverter
{
    private readonly Type[] _types;

    public CustomMetadataConverter(params Type[] types)
    {
        _types = types;
    }

    public override bool CanConvert(Type objectType)
    {
        return _types.Any(t => t == objectType);
    }

    // I've removed ReadJson and CanRead here to keep the question clear

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JToken token = JToken.FromObject(value);

        if (token.Type != JTokenType.Object)
            token.WriteTo(writer);
        else
        {
            JObject jsonObject = (JObject)token;

            IList<KeyValue> properties = jsonObject.Properties()
                .Select(p => new KeyValue { Key = p.Name, Value = p.Value.ToString() }).ToList();

            // If I change the IList<KeyValue> to anonymous array, output would be the same
            //var properties = jsonObject.Properties().Select(p => new { Key = p.Name, Value = p.Value.ToString() }).ToArray();

            jsonObject.RemoveAll();

            jsonObject.Add(new JProperty("Metadata", JToken.FromObject(properties)));
            jsonObject.WriteTo(writer);
        }
    }
}
我试图将其移动到根目录下,但自定义转换器的输出是用父目录包装的,而不是替换它。我知道这是因为自定义转换器只读取
元数据的子项
,但如果我将
CustomMetadataConverter(typeof(Metadata))
更改为
CustomMetadataConverter(typeof(Data))
则会将整个查询转换为
键/值对。这不是我想要的

这是实现自定义转换器后的输出

{
  "Metadata": [
    {
      "Metadata": [
        {
          "Key": "FirstName",
          "Value": "ABC"
        },
        {
          "Key": "LastName",
          "Value": "XYZ"
        }
      ]
    }
  ],
  "Length": 25,
  "Type": "application/mp3"
}

如果列表中有两个或多个
元数据
项,并且您将它们序列化为平面数组中的键值对对象,如您在问题中所述,那么如果您以后需要反序列化JSON,则很难判断哪些键值对属于一起。最好使用这样的二维数组结构:

{
  "Metadata": [
    [
      {
        "Key": "FirstName",
        "Value": "ABC"
      },
      {
        "Key": "LastName",
        "Value": "XYZ"
      }
    ],
    [
      {
        "Key": "FirstName",
        "Value": "DEF"
      },
      {
        "Key": "LastName",
        "Value": "MNL"
      }
    ]
  ],
  "Length": 25,
  "Type": "application/mp3"
}
下面是一个转换器,它可以实现这一点:

class ObjectToKvpArrayConverter<T> : JsonConverter where T : class
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JArray array = new JArray(
            JObject.FromObject(value)
                   .Properties()
                   .Select(jp => 
                       new JObject(
                           new JProperty("Key", jp.Name),
                           new JProperty("Value", jp.Value)
                       )
                   )
        );
        array.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = new JObject(
            JArray.Load(reader)
                  .Children<JObject>()
                  .Select(jo => new JProperty((string)jo["Key"], jo["Value"]))
        );
        T result = Activator.CreateInstance<T>();
        serializer.Populate(obj.CreateReader(), result);
        return result;
    }
}
class ObjectToKvpArrayConverter:JsonConverter其中T:class
{
公共覆盖布尔CanConvert(类型objectType)
{
返回objectType==typeof(T);
}
公共重写void WriteJson(JsonWriter编写器、对象值、JsonSerializer序列化器)
{
JArray数组=新的JArray数组(
JObject.FromObject(值)
.Properties()
.选择(jp=>
新作业项目(
新JProperty(“Key”,jp.Name),
新JProperty(“Value”,jp.Value)
)
)
);
array.WriteTo(writer);
}
公共重写对象ReadJson(JsonReader阅读器,类型objectType,对象existingValue,JsonSerializer序列化程序)
{
JObject obj=新JObject(
JArray.Load(读卡器)
.儿童()
.Select(jo=>newjproperty((字符串)jo[“Key”]、jo[“Value”]))
);
T result=Activator.CreateInstance();
填充(obj.CreateReader(),result);
返回结果;
}
}
您可以这样使用转换器:

var json = JsonConvert.SerializeObject(data, Formatting.Indented, new ObjectToKvpArrayConverter<Metadata>());
var json=JsonConvert.serialized对象(数据、格式、缩进、新ObjectToKvpArrayConverter());

这里是一个往返演示:

您的转换器正在做正确的事情。您的真实数据模型具有
IList元数据
,每个
元数据
都将被序列化为键/值对列表,这意味着
元数据
属性将被序列化为二维锯齿数组,而不是所需输出中显示的一维数组。唯一不必要的是附加的嵌套的
元数据
对象,您可以将其设置为2d数组,而不使用该数组。@abc正确。我认为应该从转换器外部进行更改。但我不知道怎么做?如果我理解正确,我应该使用另一种类型,而不是
IList
?但是什么样的适合呢?此外,我使用完全相同的模型查询我的数据库。另外,如果我从converter和return数组中删除
KeyValue
,则输出不会改变。但是什么类型合适呢?--我们;;,如果
IList
包含两个条目,您希望JSON中显示什么?只显示一个条目的结果。@dbc它是这样的:
{“Metadata”:[{“Key”:“FirstName”,“Value”:“ABC”},{“Key”:“LastName”,“Value”:“DEF”}],“Length”:25,“Type”:“application/mp3”,“Metadata”:[{“Key”:“FirstName”,“Value”:“HG”},{“Key”:“LastName”,“Value”:“XYZ”}],“Length”:26,“Type”:“application/mp4”}
这不是格式良好的JSON。将其上载到,您将收到错误。请编辑您的问题,以显示您希望通过两个
元数据
条目得到什么i、 非常感谢你的好意。它可以工作,但我很好奇,为什么只有当我从ObjectToKvpArrayConverter中删除泛型装饰时,它的输出才会像预期的那样返回。我的错!我忘了改变你的方法。
class ObjectToKvpArrayConverter<T> : JsonConverter where T : class
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(T);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        JArray array = new JArray(
            JObject.FromObject(value)
                   .Properties()
                   .Select(jp => 
                       new JObject(
                           new JProperty("Key", jp.Name),
                           new JProperty("Value", jp.Value)
                       )
                   )
        );
        array.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject obj = new JObject(
            JArray.Load(reader)
                  .Children<JObject>()
                  .Select(jo => new JProperty((string)jo["Key"], jo["Value"]))
        );
        T result = Activator.CreateInstance<T>();
        serializer.Populate(obj.CreateReader(), result);
        return result;
    }
}
var json = JsonConvert.SerializeObject(data, Formatting.Indented, new ObjectToKvpArrayConverter<Metadata>());