I fixed it myself, by using a special converter.
I've created a ICustomResponseModelAdapter which uses objects from the library itself, see below the implementation.
It's not fully JSON:API format, as we are triggering also some other entities in an endpoint for other entities (For example if Group is changed, it's possible we send an event for a User also)
using System;
using System.Collections.Generic;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.Queries.Internal;
using JsonApiDotNetCore.QueryStrings;
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Serialization.Objects;
using JsonApiDotNetCore.Serialization.Response;
namespace xxx.Api.SignalR
{
public class CustomResponseModelAdapter : ResponseModelAdapter, ICustomResponseModelAdapter
{
private readonly IJsonApiRequest _request;
private readonly IJsonApiOptions _options;
private readonly ILinkBuilder _linkBuilder;
private readonly IMetaBuilder _metaBuilder;
private readonly IResourceDefinitionAccessor _resourceDefinitionAccessor;
private readonly IEvaluatedIncludeCache _evaluatedIncludeCache;
private readonly IRequestQueryStringAccessor _requestQueryStringAccessor;
private readonly ISparseFieldSetCache _sparseFieldSetCache;
private readonly IResourceGraph _resourceGraph;
public CustomResponseModelAdapter(IJsonApiRequest request, IJsonApiOptions options, ILinkBuilder linkBuilder, IMetaBuilder metaBuilder, IResourceDefinitionAccessor resourceDefinitionAccessor, IEvaluatedIncludeCache evaluatedIncludeCache, ISparseFieldSetCache sparseFieldSetCache, IRequestQueryStringAccessor requestQueryStringAccessor, IResourceGraph resourceGraph) : base(request, options, linkBuilder, metaBuilder, resourceDefinitionAccessor, evaluatedIncludeCache, sparseFieldSetCache, requestQueryStringAccessor)
{
_resourceGraph = resourceGraph;
_request = request;
_options = options;
_linkBuilder = linkBuilder;
_metaBuilder = metaBuilder;
_resourceDefinitionAccessor = resourceDefinitionAccessor;
_evaluatedIncludeCache = evaluatedIncludeCache;
_sparseFieldSetCache = sparseFieldSetCache;
_requestQueryStringAccessor = requestQueryStringAccessor;
}
public Document ConvertSpecific(object model)
{
_sparseFieldSetCache.Reset();
Document document = new Document();
Action<ResourceObject, ResourceType, IIdentifiable> addRelations = (ResourceObject resourceObject, ResourceType resourceType, IIdentifiable resource) =>
{
foreach (JsonApiDotNetCore.Resources.Annotations.RelationshipAttribute relation in resourceType.Relationships)
{
resourceObject.Relationships ??= new Dictionary<string, RelationshipObject?>();
resourceObject.Relationships.Add(relation.PublicName, new RelationshipObject
{
Links = _linkBuilder.GetRelationshipLinks(relation, resource),
Data = default
});
}
};
if (model is IEnumerable<IIdentifiable> resources)
{
ResourceType resourceType = null;
List<ResourceObject> resourceObjects = new List<ResourceObject>();
foreach (IIdentifiable resource in resources)
{
if (resourceType == null)
resourceType = _resourceGraph.GetResourceType(resource.GetType());
ResourceObject resourceObject = ConvertResource(resource, resourceType, EndpointKind.Primary);
// Add relationships
addRelations(resourceObject, resourceType, resource);
// Add to results
resourceObjects.Add(resourceObject);
}
// Add to document
document.Data = new SingleOrManyData<ResourceObject>(resourceObjects);
}
else if (model is IIdentifiable resource)
{
ResourceType resourceType = _resourceGraph.GetResourceType(model.GetType());
ResourceObject resourceObject = ConvertResource(resource, resourceType, EndpointKind.Primary);
// Add relationships
addRelations(resourceObject, resourceType, resource);
// Add to document
document.Data = new SingleOrManyData<ResourceObject>(resourceObject);
}
else
{
throw new InvalidOperationException("Data being returned must be resources, operations, errors or null.");
}
document.JsonApi = GetApiObject();
document.Links = _linkBuilder.GetTopLevelLinks();
document.Meta = _metaBuilder.Build();
return document;
}
}
}