This serialiser uses JSON via a NuGet dependency on Json.NET.
Usage
endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
Json.NET attributes
Json.NET attributes are supported.
For example
[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; }
}
By default, the Json.NET serializer adds a Byte Order Mark (BOM). To disable it, see the custom writer section.
Custom settings
Customize the instance of JsonSerializerSettings used for serialization.
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Converters =
{
new IsoDateTimeConverter
{
DateTimeStyles = DateTimeStyles.RoundtripKind
}
}
};
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
serialization.Settings(settings);
Custom reader
Customize the creation of the JsonReader.
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
serialization.ReaderCreator(stream =>
{
var streamReader = new StreamReader(stream, Encoding.UTF8);
return new JsonTextReader(streamReader);
});
Custom writer
Customize the creation of the JsonWriter.
In the example below, the custom writer omits the Byte Order Mark (BOM).
var noBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: false);
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
serialization.WriterCreator(stream =>
{
var streamWriter = new StreamWriter(stream, noBomEncoding);
return new JsonTextWriter(streamWriter)
{
Formatting = Formatting.None
};
});
Custom content key
When using 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).
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
serialization.ContentTypeKey("custom-key");
TypeNameHandling
The NewtonsoftJsonSerializer
is using TypeNameHandling.
by default.
If TypeNameHandling.
is required it can be configured via custom JsonSerializerSettings
settings, where the TypeNameHandling
setting can be explicitly controlled.
TypeNameHandling.
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 for further details on this vulnerability.
When using TypeNameHandling.
, consider a custom SerializationBinder 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. This feature requires the TypeNameHandling
setting to be set to Auto
or All
.
See native integration with SqlTransport sample for more details.
BSON
Customize to use the Newtonsoft Bson serialization.
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
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 as shown below:
using NewtonsoftJsonSerializer = global::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:
var settings = new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Converters =
{
new XmlJsonConverter()
}
};
var serialization = endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>();
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:
Multiple messages in the same stream are not supported.
The AddDeserializer
API can help transition between serializers. See the Multiple Deserializers Sample 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. 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.
To make the serializer compatible with this behavior use the following serialization binder:
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:
endpointConfiguration.UseSerialization<NewtonsoftJsonSerializer>()
.Settings(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
SerializationBinder = new SkipAssemblyNameForMessageTypesBinder(new[]
{
typeof(MyNativeIntegrationMessage)
})
});