Method name transforms
By default, Nerdbank.JsonRpc maps CLR method names to JSON-RPC wire names by removing a trailing Async suffix (if any) and converting the result to camelCase. For example, GetValueAsync becomes getValue and Ping becomes ping. This applies symmetrically to server-side target registration (AddRpcTarget) and generated client proxies (Attach), so a contract's methods dispatch under the same wire name on both sides without any extra configuration.
[GenerateJsonRpcProxy]
[GenerateShape(IncludeMethods = MethodShapeFlags.PublicInstance)]
public partial interface ICalculator
{
// Sent on the wire as "add" by default: the trailing "Async" is removed and the
// remainder is camelCased.
ValueTask<int> AddAsync(int a, int b, CancellationToken cancellationToken);
// An explicit name is authoritative and is used verbatim, bypassing any configured transform.
[MethodShape(Name = "subtract")]
ValueTask<int> SubtractAsync(int a, int b, CancellationToken cancellationToken);
}
Explicit names are authoritative
A method annotated with [MethodShape(Name = "...")] always dispatches on that exact name, both when registering a server target and when generating a client proxy. The configured transform is not applied to it. This makes explicit names a reliable escape hatch when you need a specific wire name, such as subtract in the example above.
Interoperating with StreamJsonRpc
StreamJsonRpc's default behavior is to send and expect CLR-style method names (for example, GetValueAsync) unless its own MethodNameTransform is configured. To interoperate with such a peer, use Identity on either side, which returns names unchanged:
// Interoperate with a StreamJsonRpc peer that hasn't configured its own method name transform:
// dispatch on the CLR names it sends by default (e.g. "AddAsync"). This is an alternative
// to the default registration above, not a second registration on the same JsonRpc instance.
if (useIdentityTargetNaming)
{
rpc.AddRpcTarget<ICalculator>(calculator, new JsonRpcTargetOptions { MethodNameTransform = CommonMethodNameTransforms.Identity });
}
// Send CLR-style names (e.g. "AddAsync") to interoperate with a StreamJsonRpc peer
// that hasn't configured its own method name transform.
ICalculator interopClient = rpc.Attach<ICalculator>(new JsonRpcProxyOptions { MethodNameTransform = CommonMethodNameTransforms.Identity });
Marshaled objects
RPC-marshalable objects are registered as targets and attached as proxies implicitly. By default their methods use the same name transform as ordinary RPC contracts: the trailing Async suffix is removed and the name is camel-cased. Their naming options are independent of options supplied to an individual AddRpcTarget or Attach call.
StreamJsonRpc instead uses verbatim CLR names for RPC-marshalable interfaces. For interoperability with its default configuration, set both MarshaledTargetOptions and MarshaledProxyOptions before starting the connection:
JsonRpc rpc = new(channel)
{
MarshaledTargetOptions = new() { MethodNameTransform = CommonMethodNameTransforms.Identity },
MarshaledProxyOptions = new() { MethodNameTransform = CommonMethodNameTransforms.Identity },
};
These properties affect only RPC-marshalable objects, not ordinary RPC targets or proxies. Set the options for your ordinary contract separately if it also needs to use StreamJsonRpc's naming convention. [MethodShape(Name = "...")] still takes precedence over any transform.
Custom transforms
Set MethodNameTransform or MethodNameTransform to a Func<string, string> to fully control implicit name mapping. The function receives the CLR method name. It must return a non-null, non-empty result; registering a target throws if two methods transform to the same wire name, or if the transform returns a null or empty value.
Events use the separate EventNameTransform, which defaults to camelCase only and preserves a trailing Async suffix. Unlike method naming, the default event transform does not remove Async; see Events as notifications.