Leveraging AMQP Messaging with Solace Schema Registry: Serializing and Deserializing Messages in .NET
The SERDES Collection provides .NET libraries for efficient serialization and deserialization of structured message payloads over AMQP 1.0. These libraries enable applications to convert complex data structures into compact, transmittable formats and reconstruct them on receipt. SERDES transmits schema information through AMQP application properties rather than embedding it in the message payload, preserving schema identifiers during transmission and supporting both binary and text message formats. The examples on this page use AMQPNetLite, however the concepts apply to any AMQP 1.0 client library with appropriate adjustments.
- Prerequisites
- Deploying with NuGet
- AMQP Client Dependencies (AMQPNetLite Example)
- Using SERDES with AMQP Messaging
- Serializing and Deserializing Messages with JSON Schema over AMQP
- Troubleshooting Cross-Protocol SERDES
Prerequisites
Before implementing SERDES with AMQP messaging, you should understand the language-agnostic concepts and configuration options covered in Serialization and Deserialization with Solace Schema Registry. Key topics include:
- Connecting to Schema Registry (authentication and security)
- Choosing schema resolution strategies (Destination ID, Topic ID, Topic ID with Profiles, Record ID)
- Optimizing performance (caching and lookup options)
- Advanced configuration (auto-registration, header formats, JSON Schema-specific settings)
- Cross-protocol message translation
This page focuses on AMQP-specific implementation details for .NET, including imports, AMQPNetLite configuration, and code examples for serializing and deserializing messages over AMQP 1.0.
Deploying with NuGet
To use the SERDES Collection with your AMQP applications, you need to include the appropriate NuGet packages in your project. The SERDES libraries are distributed as separate packages that you can include based on your serialization format requirements.
The following sections show NuGet dependencies for the AMQPNetLite implementation of AMQP. If you're using a different AMQP client library, you would need to include the appropriate dependencies for that implementation instead of or in addition to the AMQPNetLite client shown here.
The following sections show you how to configure your .NET project to include the necessary dependencies for schema registry integration:
JSON Schema SERDES Dependencies
The JSON Schema SERDES provides a specific implementation for JSON Schema serialization and deserialization. This dependency includes the JsonSchemaSerializer<T>, JsonSchemaDeserializer<T>, and JSON Schema-specific configuration properties, and also brings in the required common SERDES components used across all schema formats.
Add the following package reference to your .NET project file:
<ItemGroup>
<PackageReference Include="Solace.SchemaRegistry.Serdes.JsonSchema" Version="1.0.0" />
</ItemGroup>
For the latest version information and additional details about the JSON Schema SERDES package, see the NuGet Gallery.
AMQP Client Dependencies (AMQPNetLite Example)
For AMQP messaging with SERDES using AMQPNetLite, you need to include the appropriate client library. This client provides AMQP 1.0 protocol implementation and is lightweight and easy to use. Other AMQP client libraries would have their own dependency requirements and configuration approaches.
Add the following package reference to your .NET project file:
<ItemGroup>
<!-- AMQPNetLite Client -->
<PackageReference Include="AMQPNetLite" Version="2.4.5" />
</ItemGroup>
For the latest version information, see the NuGet Gallery.
Required Using Statements
To use the SERDES Collection with AMQP messaging, include the following namespaces in your .NET application:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Amqp; using Amqp.Framing; using Solace.SchemaRegistry.Serdes.JsonSchema; using Solace.Serdes;
Using SERDES with AMQP Messaging
AMQP messaging provides a standards-based approach to message publishing and consumption that integrates with the SERDES Collection. When using SERDES with AMQP messaging, schema information is transmitted through AMQP application properties rather than being embedded in the message payload, enabling efficient schema resolution and cross-protocol compatibility with REST and Solace Messaging API applications.
General AMQP Concepts with SERDES
When implementing SERDES with any AMQP 1.0 client, there are several important protocol-level considerations:
- Application Properties—SERDES headers are transmitted as AMQP application properties, which ensures schema identifiers and metadata are preserved during message transmission. This is a standard feature of the AMQP 1.0 protocol that all compliant clients support.
- Message Type Support—AMQP messaging supports both binary and text message formats for SERDES operations. However, the SERDES deserializer only operates on binary payloads. Applications must extract or convert the message content into a binary format before passing it to the deserializer.
- Cross-Protocol Compatibility—AMQP applications can seamlessly exchange messages with REST and Solace .NET API applications through the Solace broker, which automatically translates message formats and preserves SERDES headers across protocols. For more information on cross-protocol message translation, see AMQP.
AMQPNetLite Implementation Considerations
When using AMQPNetLite as your AMQP client implementation, there are additional specific considerations:
- Application Properties Access—AMQPNetLite provides direct access to AMQP application properties through the
Message.ApplicationPropertiesproperty, which is anApplicationPropertiesobject containing aMapproperty for accessing key-value pairs. - Message Body Handling—With AMQPNetLite, message bodies can be accessed as
Dataobjects (which contain aBinaryproperty with the byte array) or directly as byte arrays depending on how the message was constructed. The deserializer requires a byte array. - Async Support—AMQPNetLite supports asynchronous operations for sending and receiving messages. The SERDES libraries also provide async methods (
SerializeAsyncandDeserializeAsync) for non-blocking operations.
AMQP Connection Configuration (AMQPNetLite Example)
When implementing AMQP applications that use SERDES with AMQPNetLite, configure the connection, session, and sender/receiver links according to AMQPNetLite documentation. The key requirement for SERDES is ensuring that application properties containing schema information are properly handled during message transmission.
Example connection and sender link creation:
var connectionFactory = new ConnectionFactory();
var address = new Address($"amqp://{username}:{password}@{host}:{port}");
var connection = await connectionFactory.CreateAsync(address);
var session = new Session(connection);
var sender = new SenderLink(session, "json-schema-producer", $"topic://{topicName}");
// Use sender for SERDES operations (see examples below)
// Clean up resources when done
sender.Close();
session.Close();
connection.Close();
Use try/finally blocks to ensure AMQP resources are properly closed even if exceptions occur. Cleanup order should be: sender/receiver → session → connection (innermost to outermost).
Cross-Protocol Message Flow with SERDES
AMQP messaging integrates seamlessly with other protocols through SERDES. For detailed information about how AMQP messages flow to and from REST and Solace Messaging API consumers, including complete message flow examples, see Cross-Protocol Message Flow Examples.
Key AMQP-specific considerations for cross-protocol messaging with .NET:
- AMQP uses native application properties for SERDES headers (no prefix required).
- AMQPNetLite provides direct access via
Message.ApplicationPropertiesproperty. - Message body can be
Dataobjects (withBinaryproperty) or direct byte arrays. - AMQP application properties are automatically mapped to HTTP headers (with
solace-user-property-prefix) for REST consumers. - AMQP application properties are automatically mapped to SMF user properties for Solace Messaging API consumers.
Serializing and Deserializing Messages with JSON Schema over AMQP
The following examples demonstrate how to implement JSON Schema serialization and deserialization in AMQP messaging applications. These examples use AMQPNetLite as the AMQP client implementation, but the core concepts can be applied to other AMQP 1.0 client libraries with appropriate adjustments for their specific APIs.
Publishing JSON Schema Messages over AMQP (AMQPNetLite Example)
When publishing JSON Schema messages over AMQP using AMQPNetLite, create a JsonSchemaSerializer<T>, configure it with Schema Registry connection details (see Getting Started with SERDES), and serialize message payloads before sending them.
Both JsonSchemaSerializer<T> and JsonSchemaDeserializer<T> implement IDisposable and must be properly disposed when no longer needed. Always use a using statement or explicitly call Dispose() to ensure that resources are released and cached data is cleaned up.
The key SERDES integration point is setting schema metadata as AMQP application properties. Example serialization and message publishing:
// Create a User object to serialize
var user = new User
{
Name = "John Doe",
Id = "123",
Email = "support@solace.com"
};
// Serialize - this populates the headers dictionary with schema information
Dictionary<string, object> headers = new Dictionary<string, object>();
byte[] payloadBytes = await serializer.SerializeAsync(topicName, user, headers);
// Create AMQP message with serialized payload
var message = new Message(payloadBytes)
{
Properties = new Properties() { ContentType = "application/json", Subject = topicName }
};
// CRITICAL: Set SERDES headers as AMQP application properties
var applicationProperties = new ApplicationProperties();
foreach (var entry in headers)
{
applicationProperties[entry.Key] = entry.Value;
}
message.ApplicationProperties = applicationProperties;
await sender.SendAsync(message);
For a complete list of JSON Schema SERDES properties and methods, see the .NET JSON SERDES API Reference.
Consuming JSON Schema Messages over AMQP (AMQPNetLite Example)
When consuming JSON Schema messages over AMQP using AMQPNetLite, create a JsonSchemaDeserializer<T> configured with Schema Registry connection details (see Getting Started with SERDES). The key SERDES integration points are extracting headers from AMQP application properties and handling different message body types.
Exception Types
When deserializing messages, the DeserializeAsync method can throw the following exceptions:
JsonSchemaValidationException(fromSolace.Serdesnamespace)—Thrown when the message payload does not conform to the JSON Schema retrieved from the registry. This indicates a schema validation failure.SerializationException(fromSolace.Serdesnamespace)—Thrown when the message payload cannot be decoded or deserialized, such as when the payload is malformed or the schema cannot be resolved from the registry.
Example SERDES header extraction and deserialization:
var message = await receiver.ReceiveAsync(TimeSpan.FromSeconds(1));
if (message != null)
{
try
{
// Extract payload - handle both Data objects and byte arrays
byte[] payloadBytes = message.Body is Data data ? data.Binary :
message.Body is byte[] bytes ? bytes : null;
if (payloadBytes == null)
{
receiver.Reject(message);
continue;
}
// CRITICAL: Extract SERDES headers from AMQP application properties
Dictionary<string, object> headers = new Dictionary<string, object>();
if (message.ApplicationProperties != null)
{
foreach (var key in message.ApplicationProperties.Map.Keys)
{
headers[key.ToString()] = message.ApplicationProperties.Map[key];
}
}
// Deserialize using JSON Schema validation
User user = await deserializer.DeserializeAsync(topicName, payloadBytes, headers);
Console.WriteLine($"Got message: Name={user.Name}, Id={user.Id}, Email={user.Email}");
receiver.Accept(message);
}
catch (JsonSchemaValidationException ve)
{
Console.WriteLine($"Validation error: {ve.Message}");
receiver.Reject(message);
}
catch (SerializationException ex)
{
Console.WriteLine($"Decoding error: {ex.Message}");
receiver.Reject(message);
}
}
For a complete list of JSON Schema SERDES properties and methods, see the .NET JSON SERDES API Reference.
Preventing Head-of-Line Blocking in AMQP Consumers
When consuming messages with AMQP and SERDES, it's important to properly acknowledge or reject messages to prevent head-of-line blocking. With AMQPNetLite, you use receiver.Accept(message) for successfully processed messages and receiver.Reject(message) for messages that cannot be processed.
Key considerations for message settlement:
- Accept Successfully Processed Messages—Call
receiver.Accept(message)after successfully deserializing and processing a message to acknowledge it. - Reject Invalid Messages—Call
receiver.Reject(message)for messages with validation errors, deserialization failures, or unexpected body types. This prevents the consumer from blocking on messages it cannot process. - Handle Exceptions Appropriately—Catch both
JsonSchemaValidationException(for schema validation errors) andSerializationException(for deserialization errors) and reject the message in both cases. - Continuous Receive Loop—Implement a continuous receive loop with appropriate timeout handling to process messages as they arrive.
Example message settlement pattern:
while (!exitRequested)
{
try
{
// Receive a message with a timeout
var message = await receiver.ReceiveAsync(TimeSpan.FromSeconds(1));
if (message != null)
{
try
{
// Extract payload and deserialize
// ...
User user = await deserializer.DeserializeAsync(topicName, payloadBytes, headers);
// Process the message
Console.WriteLine($"Got message: Name={user.Name}, Id={user.Id}");
// Accept the message to acknowledge receipt
receiver.Accept(message);
}
catch (JsonSchemaValidationException ve)
{
Console.WriteLine($"Validation error: {ve.Message}");
// Reject the message due to validation error
receiver.Reject(message);
}
catch (SerializationException ex)
{
Console.WriteLine($"Decoding error: {ex.Message}");
// Reject the message due to decoding error
receiver.Reject(message);
}
}
}
catch (Exception ex) when (!(ex is TaskCanceledException))
{
Console.WriteLine($"Error receiving message: {ex.Message}");
// Continue with the next message
}
}
Important considerations for message settlement:
- If you neither accept nor reject a message, it remains unacknowledged and may block subsequent messages from being delivered (head-of-line blocking).
- For messages that fail validation or deserialization, rejecting them allows the consumer to continue processing subsequent messages.
- Consider implementing a dead-letter queue or logging mechanism for rejected messages to track problematic payloads.
- The exception filter
when (!(ex is TaskCanceledException))prevents logging normal cancellation events while still handling other exceptions.
Troubleshooting Cross-Protocol SERDES
When working with mixed AMQP, REST, and Solace .NET API environments using SERDES, you may encounter specific issues related to message type handling, application properties, and cross-protocol compatibility. This section provides guidance for common troubleshooting scenarios, with a focus on AMQPNetLite implementation but including general AMQP considerations as well.
Common Issues and Solutions
- General AMQP Considerations—Ensure your AMQP client properly handles application properties, as these are used to transmit schema information. Different AMQP clients may have different APIs for setting and retrieving these properties.
- Missing SERDES Headers—With AMQPNetLite, verify that SERDES headers are being set as AMQP application properties using the
ApplicationPropertiesclass and that headers are being extracted properly by iterating through theApplicationProperties.Map. With other AMQP clients, ensure you're properly setting and retrieving application properties according to that client's API. - Cross-Protocol Header Mapping Issues—Ensure that AMQP application properties are being mapped correctly to other protocol headers:
- For AMQP to REST: Application properties should appear as HTTP headers with
solace-user-property-prefix. - For AMQP to Solace .NET API: Application properties should appear as SMF user properties.
- For AMQP to REST: Application properties should appear as HTTP headers with
- Message Body Type Issues—Ensure that your AMQP consumers can handle both
Dataobjects (withBinaryproperty) and direct byte array message bodies, as cross-protocol scenarios may result in different message body types depending on the publishing protocol and content type. - Async/Await Patterns—Ensure that you're properly using async/await with the SERDES async methods (
SerializeAsyncandDeserializeAsync) to avoid blocking the thread and potential deadlocks. - Exception Handling—Make sure to catch both
JsonSchemaValidationException(for schema validation errors) andSerializationException(for general serialization/deserialization errors) when working with JSON Schema SERDES.