# Json.NET Serializer This serializer uses [JSON](https://en.wikipedia.org/wiki/Json) via a NuGet dependency on [Json.NET](https://www.newtonsoft.com/json). ## Usage ```cs endpointConfiguration.UseSerialization(); ``` ### Json.NET attributes Json.NET attributes are supported. For example ```cs [JsonObject(MemberSerialization.OptIn)] public class CreatePersonMessage : IMessage { // "John Smith" [JsonProperty] public string Name { get; set; } // "2000-12-15T22:11:03" [JsonProperty] public DateTime BirthDate { get; set; } // new Date(976918263055) [JsonProperty] [JsonConverter(typeof(JavaScriptDateTimeConverter))] public DateTime LastModified { get; set; } // not serialized because mode is opt-in public string Department { get; set; } } ``` > [!NOTE] > By default, the Json.NET serializer adds a Byte Order Mark (BOM). To disable it, see the [custom writer](/nservicebus/serialization/newtonsoft.md#usage-custom-writer) section. ### Custom settings Customize the instance of [JsonSerializerSettings](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonSerializerSettings.htm) used for serialization. ```cs var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Converters = { new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.RoundtripKind } } }; var serialization = endpointConfiguration.UseSerialization(); serialization.Settings(settings); ``` ### Custom reader Customize the creation of the [JsonReader](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonReader.htm). ```cs var serialization = endpointConfiguration.UseSerialization(); serialization.ReaderCreator(stream => { var streamReader = new StreamReader(stream, Encoding.UTF8); return new JsonTextReader(streamReader); }); ``` ### Custom writer Customize the creation of the [JsonWriter](https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonWriter.htm). In the example below, the custom writer omits the [Byte Order Mark (BOM)](https://en.wikipedia.org/wiki/Byte_order_mark). ```cs var noBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false); var serialization = endpointConfiguration.UseSerialization(); serialization.WriterCreator(stream => { var streamWriter = new StreamWriter(stream, noBomEncoding); return new JsonTextWriter(streamWriter) { Formatting = Formatting.None }; }); ``` ### Custom content key When using [additional deserializers](/nservicebus/serialization/index.md#specifying-additional-deserializers) or transitioning between different versions of the same serializer it can be helpful to take explicit control over the content type a serializer passes to NServiceBus (to be used for the [ContentType header](/nservicebus/messaging/headers.md#serialization-headers-nservicebus-contenttype)). ```cs var serialization = endpointConfiguration.UseSerialization(); serialization.ContentTypeKey("custom-key"); ``` ## TypeNameHandling The `NewtonsoftJsonSerializer` is using `TypeNameHandling.None` by default. If `TypeNameHandling.Auto` is required it can be configured via [custom `JsonSerializerSettings` settings](#usage-custom-settings), where the `TypeNameHandling` setting can be explicitly controlled. > [!WARNING] > `TypeNameHandling.Auto` can be a security risk as it allows the message payload to control the deserialization target type. See [CA2326: Do not use TypeNameHandling values other than None](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2326) for further details on this vulnerability. When using `TypeNameHandling.Auto`, consider a [custom SerializationBinder](https://www.newtonsoft.com/json/help/html/SerializeSerializationBinder.htm) to limit the allowed deserialization types. ## Inferring message type from $type For integration scenarios where the sender is unable to add message headers, the serializer is able to infer the message type from the [`$type` property supported by Json.NET](https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm). This feature requires the `TypeNameHandling` setting to be set to `Auto` or `All`. See [native integration with SqlTransport sample](/samples/sqltransport/native-integration/index.md) for more details. ## BSON Customize to use the [Newtonsoft Bson serialization](https://www.newtonsoft.com/json/help/html/SerializeToBson.htm). ```cs var serialization = endpointConfiguration.UseSerialization(); serialization.ReaderCreator(stream => new BsonDataReader(stream)); serialization.WriterCreator(stream => new BsonDataWriter(stream)); ``` ## Compatibility with the core JSON serializer Up to NServiceBus version 6, a JSON serializer based on Json.NET was bundled with the core package. This section outlines the compatibility considerations when switching to this serializer. ### No support for `XContainer` and `XDocument` properties In contrast to the bundled serializer `XContainer` and `XDocument` properties are no longer supported. If `XContainer` and `XDocument` properties are required [use a JsonConverter](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm) as shown below: ```cs using NewtonsoftJsonSerializer = Newtonsoft.Json.JsonSerializer; class XmlJsonConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object value, NewtonsoftJsonSerializer serializer) { var xcontainer = (XContainer) value; writer.WriteValue(xcontainer.ToString(SaveOptions.DisableFormatting)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, NewtonsoftJsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) { return null; } if (reader.TokenType != JsonToken.String) { throw new Exception($"Unexpected token or value when parsing XContainer. Token: {reader.TokenType}, Value: {reader.Value}"); } var value = (string) reader.Value; if (objectType == typeof(XDocument)) { try { return XDocument.Load(new StringReader(value)); } catch (Exception exception) { throw new Exception($"Error parsing XContainer string: {reader.Value}", exception); } } return XElement.Load(new StringReader(value)); } public override bool CanConvert(Type objectType) { return typeof(XContainer).IsAssignableFrom(objectType); } } ``` Configure the converter as follows: ```cs var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Converters = { new XmlJsonConverter() } }; var serialization = endpointConfiguration.UseSerialization(); serialization.Settings(settings); ``` ### No support for bundled logical messages This serializer is not compatible with multiple bundled messages (when using the `Send(object[] messages)` APIs) sent from NServiceBus version 3 and below. If this scenario is detected then an exception with the following message will be thrown: ```txt Multiple messages in the same stream are not supported. ``` The `AddDeserializer` API can help transition between serializers. See the [Multiple Deserializers Sample](/samples/serializers/multiple-deserializers/index.md) for more information. ### Use of $type requires an assembly qualified name The bundled serializer registers a custom serialization binder in order to not require the assembly name to be present when inferring message type from the [`$type` property supported by json.net](https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm). Not having to specify the assembly name can be useful to reduce coupling when using the serializer for native integration scenarios as [demonstrated in this sample](/samples/sqltransport/native-integration/index.md). To make the serializer compatible with this behavior use the following serialization binder: ```cs class SkipAssemblyNameForMessageTypesBinder : ISerializationBinder { Type[] messageTypes; public SkipAssemblyNameForMessageTypesBinder(Type[] messageTypes) { this.messageTypes = messageTypes; } public Type BindToType(string assemblyName, string typeName) { return messageTypes.FirstOrDefault(messageType => messageType.FullName == typeName); } public void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = serializedType.Assembly.FullName; typeName = serializedType.FullName; } } ``` and configured as follows: ```cs endpointConfiguration.UseSerialization() .Settings(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, SerializationBinder = new SkipAssemblyNameForMessageTypesBinder(new[] { typeof(MyNativeIntegrationMessage) }) }); ```