如何将具有多个字符串属性的对象序列化为每个具有一个字符串 属性 的 json 对象数组?

How to serialize an object with multiple string properties to a json array of objects with one string property each?

我有 Property 类 的列表,如下所示:

public class RootObject
{
    public List<Property> Properties { get; set; }
}

public class Property
{
    public string MyFirstProp { get; set; }
    public string MySecondProp {get; set; }
}

当使用 Json.NET 序列化时,输出是这样的:

{
   "Properties":[
      {
         "MyFirstProp":"Hello",
         "MySecondProp":"World"
      }
   ]
}

我需要输出如下所示:

{
   "Properties":[
      {
         "MyFirstProp":"Hello"
      },
      {
         "MySecondProp":"World"
      }
   ]
}

我怎样才能做到这一点?

您可以引入一个 custom JsonConverter<Property>Property 个实例序列化为 single-property 个对象的数组,而不是单个对象:

public class ObjectAsObjectArrayConverter<TObject> : JsonConverter<TObject>
{
    public override void WriteJson(JsonWriter writer, TObject value, JsonSerializer serializer)
    {
        var contract = (serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract) ?? throw new ArgumentException("Wrong contract type");
        writer.WriteStartArray();
        foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
        {
            var propertyValue = property.ValueProvider.GetValue(value);
            if (propertyValue == null && (serializer.NullValueHandling == NullValueHandling.Ignore || property.NullValueHandling == NullValueHandling.Ignore))
                continue;
            writer.WriteStartObject();
            writer.WritePropertyName(property.PropertyName);
            if (propertyValue == null)
                writer.WriteNull();
            else if (property.Converter != null && property.Converter.CanWrite)
                property.Converter.WriteJson(writer, propertyValue, serializer);
            else
                serializer.Serialize(writer, propertyValue);
            writer.WriteEndObject();
        }
        writer.WriteEndArray();
    }

    protected virtual bool ShouldSerialize(JsonProperty property, object value) =>
        property.Readable && !property.Ignored && (property.ShouldSerialize == null || property.ShouldSerialize(value));

    public override TObject ReadJson(JsonReader reader, Type objectType, TObject existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (existingValue == null)
            existingValue = (TObject)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();

        switch (reader.MoveToContentAndAssert().TokenType)
        {
            case JsonToken.Null:
                return (TObject)(object)null;
        
            case JsonToken.StartArray:
                while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
                {
                    switch (reader.TokenType)
                    {
                        case JsonToken.StartObject:
                            serializer.Populate(reader, existingValue);
                            break;
                        default:
                            throw new JsonSerializationException("Unexpected token type " + reader.TokenType.ToString());
                    }
                }
                break;

            case JsonToken.StartObject:
                serializer.Populate(reader, existingValue);
                break;

            default:
                throw new JsonSerializationException("Unexpected token type " + reader.TokenType.ToString());
        }
        return existingValue;
    }
}

public static partial class JsonExtensions
{
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

然后修改RootObject如下,将public List<Property> Properties替换为单个public Property Properties

public class RootObject
{
    public Property Properties { get; set; }
}

并通过将转换器添加到 JsonSerializerSettings.Converters:

来序列化
var settings = new JsonSerializerSettings
{
    Converters = { new ObjectAsObjectArrayConverter<Property>() },
};
var json = JsonConvert.SerializeObject(myClass, Formatting.Indented, settings);

你会得到:

{
  "Properties": [
    {
      "MyFirstProp": "Hello"
    },
    {
      "MySecondProp": "World"
    }
  ]
}

演示 fiddle here.

要创建您需要的输出,您必须使用此代码

    var props = new Root {
    Properties = new List<Dictionary<string, string>>
    {
        new Dictionary<string,string> { { "MyFirstProp","Hello"}},
        new Dictionary<string,string> { { "MySecondProp","World"}}
    }};

    var json = JsonConvert.SerializeObject(props, Newtonsoft.Json.Formatting.Indented);

public class Root
{
    public List<Dictionary<string, string>> Properties { get; set; }
}

结果

{
  "Properties": [
    {
      "MyFirstProp": "Hello"
    },
    {
      "MySecondProp": "World"
    }
  ]
}