Serializing Object with Multiple String Properties to JSON Array in C#
By FoxLearn 1/14/2025 7:18:13 AM 83
Suppose you have a RootObject
class containing a list of Property
objects like the following:
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 looks like this:
{ "Properties": [ { "MyFirstProp": "Hello", "MySecondProp": "World" } ] }
However, you'd like the output to look like this:
{ "Properties": [ { "MyFirstProp": "Hello" }, { "MySecondProp": "World" } ] }
The goal is to serialize each property of Property
as a separate object in the JSON array.
To achieve this, you'll need to create a custom JsonConverter
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) { if (reader.TokenType == JsonToken.StartObject) { serializer.Populate(reader, existingValue); } else { 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; } }
Extensions for Handling JSON Readers
Next, we add some extension methods to handle JSON reading efficiently:
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) reader.ReadAndAssert(); while (reader.TokenType == JsonToken.Comment) 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; } }
Updating the RootObject
With the custom converter in place, you can now modify your RootObject
class to handle a single Property
rather than a list:
public class RootObject { public Property Properties { get; set; } }
Serializing the Object
Finally, serialize your RootObject
using the custom converter by adding it to the JsonSerializerSettings.Converters
collection:
var settings = new JsonSerializerSettings { Converters = { new ObjectAsObjectArrayConverter<Property>() }, }; var json = JsonConvert.SerializeObject(myClass, Formatting.Indented, settings); Console.WriteLine(json);
Output
When serialized with the above setup, the output will look like this:
{ "Properties": [ { "MyFirstProp": "Hello" }, { "MySecondProp": "World" } ] }
By using a custom JsonConverter
, we can serialize complex objects with multiple properties into a JSON array of objects with one property each. This approach allows for flexible and customized JSON serialization, which can be crucial in certain scenarios such as API integrations or data transformations.
- How to fix 'Failure sending mail' in C#
- How to Parse a Comma-Separated String from App.config in C#
- How to convert a dictionary to a list in C#
- How to retrieve the Executable Path in C#
- How to validate an IP address in C#
- How to retrieve the Downloads Directory Path in C#
- C# Tutorial
- Dictionary with multiple values per key in C#