Home > OS >  Serialize list of objects with string properties to json array of objects
Serialize list of objects with string properties to json array of objects

Time:05-24

I have list of Property classes that looks like this:

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

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

When serialized using Json.NET, the output is this:

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

I would need the output to look like this:

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

How can I accomplish this?

CodePudding user response:

You can introduce a custom JsonConverter<Property> that serializes Property instances as arrays of single-property objects, rather than as a single object:

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;
    }
}

Then modify RootObject as follows, replacing public List<Property> Properties with a single public Property Properties:

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

And serialize by adding the converter to JsonSerializerSettings.Converters:

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

And you will get:

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

Demo fiddle here.

CodePudding user response:

to create the output you need, you have to use this code

    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; }
}

result

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