When fetching data from a database, we have a lot of recursive and circular references. To handle this we configure the (default) json serializer to give the objects ids and refs, e.g..
{
"$id": "1",
"Children":
[
{ "$id": "2", "Children": null, "Name": "Bob Smith", "Parent": { "$ref": "1" }},
{ "$id": "3", "Children": null, "Name": "Suzy Smith", "Parent": { "$ref": "1" }}
],
"Name": "John Smith",
"Parent": null
},
{
"$ref": "2"
},
{
"$ref": "3"
}
]
My question regards the behaviour of the $id and $ref numbers when there are a lot of requests. We handle a decent amount of traffic and they would overflow fairly quickly.
We configure it as
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
});
Related
So I'm trying to create an Azure Logic App which will create a new record inside a Cosmos DB collection to store review results.
I've created a Cosmos DB database (called icecream) + collection (called reviews). The PartitionKey of the reviews collection is /flavorIdentifier.
In my Logic App I have a step which inserts a new document.
The code looks like this.
"Create_review": {
"inputs": {
"body": {
"flavor": "#{body('JSON_parseren_2')?['flavor']}",
"flavorIdentifier": "#{body('JSON_parseren')?['itemOrdered']}",
"id": "#{guid()}",
"review": "#{body('E-mail_met_opties_verzenden')?['SelectedOption']}"
},
"headers": {
"x-ms-documentdb-raw-partitionkey": "#body('JSON_parseren')?['itemOrdered']"
},
"host": {
"connection": {
"name": "#parameters('$connections')['documentdb']['connectionId']"
}
},
"method": "post",
"path": "/dbs/#{encodeURIComponent('icecream')}/colls/#{encodeURIComponent('reviews')}/docs"
},
"runAfter": {
"E-mail_met_opties_verzenden": [
"Succeeded"
]
},
"type": "ApiConnection"
},
So, I'm posting the message to Cosmos DB
{
"flavor": "My flavor",
"flavorIdentifier": "3",
"id": "4927042a-faa1-4e09-9c6d-d038ef659b25",
"review": "Very satisfied"
}
As you can see, I also specified the partition key to the same value as flavorIdentifier.
My guess is this should just work. But it doesn't
The error I'm receiving states
PartitionKey extracted from document doesn't match the one specified in the header
Which is strange, as both have the same value, also when checking the raw input and output. Is there anything I'm overlooking?
For completeness, the full input and output of this step.
The raw input of the failed step.
{
"host": {
"connection": {
"name": "/subscriptions/3b3729b4-021a-48b5-a2eb-47be0c7e7f44/resourceGroups/developerday-workshop/providers/Microsoft.Web/connections/documentdb"
}
},
"method": "post",
"path": "/dbs/icecream/colls/reviews/docs",
"headers": {
"x-ms-documentdb-raw-partitionkey": "3"
},
"body": {
"flavor": "My flavor",
"flavorIdentifier": "3",
"id": "4927042a-faa1-4e09-9c6d-d038ef659b25",
"review": "Very satisfied"
}
}
The raw output of the failed step.
{
"statusCode": 400,
"headers": {
"x-ms-last-state-change-utc": "Wed,27 Mar 2019 05:03:54.568 GMT",
"lsn": "1",
"x-ms-schemaversion": "1.7",
"x-ms-quorum-acked-lsn": "1",
"x-ms-substatus": "1001",
"x-ms-current-write-quorum": "3",
"x-ms-current-replica-set-size": "4",
"x-ms-xp-role": "1",
"x-ms-global-Committed-lsn": "1",
"x-ms-number-of-read-regions": "0",
"x-ms-transport-request-id": "1",
"x-ms-cosmos-llsn": "1",
"x-ms-cosmos-quorum-acked-llsn": "1",
"x-ms-session-token": "1",
"x-ms-request-charge": "1.24",
"x-ms-serviceversion": "version=2.2.0.0",
"x-ms-activity-id": "bd428162-ec1a-4f50-879a-04b8ca0817a1",
"Strict-Transport-Security": "max-age=31536000",
"x-ms-gatewayversion": "version=2.2.0.0",
"Timing-Allow-Origin": "*",
"x-ms-apihub-cached-response": "false",
"Date": "Wed, 27 Mar 2019 11:39:57 GMT",
"Content-Length": "1149",
"Content-Type": "application/json"
},
"body": {
"code": "BadRequest",
"message": "Message: {\"Errors\":[\"PartitionKey extracted from document doesn't match the one specified in the header\"]}\r\nActivityId: bd428162-ec1a-4f50-879a-04b8ca0817a1, Request URI: /apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/9d84c048-383b-498e-8472-1f57da72135d/partitions/0f29761a-9ffc-4560-94ce-0328c3c79f92/replicas/131981366274575376p/, RequestStats: \r\nRequestStartTime: 2019-03-27T11:39:57.0795308Z, RequestEndTime: 2019-03-27T11:39:57.0995601Z, Number of regions attempted: 1\r\nResponseTime: 2019-03-27T11:39:57.0995601Z, StoreResult: StorePhysicalAddress: rntbd://cdb-ms-prod-westeurope1-fd21.documents.azure.com:16833/apps/9bc5d0cc-9b7c-4b1d-9be2-0fa2654271c4/services/9d84c048-383b-498e-8472-1f57da72135d/partitions/0f29761a-9ffc-4560-94ce-0328c3c79f92/replicas/131981366274575376p/, LSN: 1, GlobalCommittedLsn: 1, PartitionKeyRangeId: 0, IsValid: True, StatusCode: 400, SubStatusCode: 1001, RequestCharge: 1.24, ItemLSN: -1, SessionToken: 1, UsingLocalLSN: False, TransportException: null, ResourceType: Document, OperationType: Create\r\n, SDK: Microsoft.Azure.Documents.Common/2.2.0.0"
}
}
Any thoughts?
Please add double quotation marks "" around your partition key and it works fine for me.
View the code:
I have a similar question as JMS Serializer serialize object in object with diffrent view, but I can't get it work like in the accepted answer.
I have a User model that has many Reviews, but the Reviews owner is another User. I have a serialization policy that outputs the following:
{
"id": "1",
"name": "John Doe",
"reviews": [
{
"id": "1",
"rate": "5",
"evaluator": {
"id": "2",
"name": "Alice",
"reviews": [...]
}
}, ...
]
}
The behavior makes sense since the associated (Review) owner model is the same as the parent model and therefore it's using the same serialization policy. But how could I define a custom serialization policy for the nested model so outputs me the following:
{
"id": "1",
"name": "John Doe",
"reviews": [
{
"id": "1"
"rate": "5",
"evaluator": "Alice"
}, ...
]
}
I'm thinking of using the JSONAPI standard for the design of our API. One thing this API must be able to do, is accept a compound document (several layers deep) and create it. The root object owns all descendants ('to-many' relationships) which the server knows nothing about at that point, so it's not possible for the client to provide an id.
Is this supported by the specification or does the client have to issue http requests for every object in the document in order?
from http://jsonapi.org/format/#document-compound-documents
Compound documents require "full linkage", meaning that every included
resource MUST be identified by at least one resource identifier object
in the same document. These resource identifier objects could either
be primary data or represent resource linkage contained within primary
or included resources. The only exception to the full linkage
requirement is when relationship fields that would otherwise contain
linkage data are excluded via sparse fieldsets.
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"links": {
"self": "http://example.com/articles/1"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"first-name": "Dan",
"last-name": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}
I have a resource (e.g. posts) which has a to-many relationship to other resources (e.g. comments). I do not need any fields of the related resource but their self-links (to fetch them asynchronously on demand). The response should look something like this:
{
"links": {
"self": "http://example.com/posts/1?xxx",
},
"data": [{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"comments": {
"links": {
"self": "http://example.com/posts/1/relationships/comments",
"related": "http://example.com/posts/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
},
"links": {
"self": "http://example.com/posts/1"
}
}],
"included": [{
"type": "comments",
"id": "5",
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"links": {
"self": "http://example.com/comments/12"
}
}]
}
My question is how should the URL for the request look like?
My idea would be to include the comments and then use an empty sparse fieldset to avoid getting any comment fields but just the self link (+id, type).
Hence, it should look something like http://example.com/posts/1?include=comments&fields[comments]=[].
So I need something like an empty sparse fieldset.
The JSON API specification does not say much about sparse fieldsets (http://jsonapi.org/format/#fetching-sparse-fieldsets) and their relation to links.
The JSON API was able to answer my question.
In short, the correct way to specify an empty sparse fieldset would be:
http://example.com/posts/1?include=comments&fields[comments]=
There's a discussion going on about whether to include the links to individual relationship items in the relationship object:
{
"links": {
"self": "http://example.com/posts/1",
},
"data": [{
"type": "posts",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"comments": {
"links": {
"self": "http://example.com/posts/1/relationships/comments",
"related": "http://example.com/posts/1/comments",
"item": [ "http://example.com/comments/5", "http://example.com/comments/12" ]
},
"data": [{
"type": "comments",
"id": "5",
"links": {
"about": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"links": {
"about": "http://example.com/comments/12"
}
}]
}
},
"links": {
"self": "http://example.com/posts/1"
}
}]
}
I use the self href from my HAL resources on the client side to find the right path for the CRUD operations. In single(ton) resources this is working fine (see the address resource below, the _links containing the self href is included in the embedded resource) but when it comes down to collections this is a different story. The _links of a collection are not rendered when the collection is in _embedded.
Earlier I worked around this problem by reading the url form the first child. But this is not sufficient. In case the collection is empty I only have
an empty array with no possibility to extract the url like that. If I want to create a new item in the collection I would like that my client knows where to send data using POST by reading the self href from _links. Is it a good idea to include the _links to my collection like this:
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1"
}
},
"_embedded": {
"contacts": {
Now I have access to the self href here:
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts"
}
},
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts/2"
}
},
"id": "2",
"name": "John Smith"
},
{
"_links": {
"self": {
"href": "http://example.org/api/v1/users/1/contacts/3"
}
},
"id": "3",
"name": "Jane Doe"
}
],
}
},
"address": {
"_links": {
"self": {
"href": "http://example.com/api/v1/addresses/1"
}
},
"street": "Bakerstreet 11",
"postal code": "123456",
"city": "Some city",
"country": "Some country",
}
},
"id": "1",
"name": "John Doe"
}
EDIT (one year later)
In the end I solved this by always adding the links of embedded resources to the parent resource. So in the example above my response object would look like this:
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1"
},
"contacts": {
"href": "http://example.com/api/v1/users/1/contacts"
},
"address": {
"href": "http://example.com/api/v1/addresses/1"
}
},
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "http://example.com/api/v1/users/1/contacts/2"
}
},
"id": "2",
"name": "John Smith"
},
{
"_links": {
"self": {
"href": "http://example.org/api/v1/users/1/contacts/3"
}
},
"id": "3",
"name": "Jane Doe"
},
],
"address": {
"_links": {
"self": {
"href": "http://example.org/api/v1/addresses/1"
}
},
"street": "Bakerstreet 11",
"postal code": "123456",
"city": "Some city",
"country": "Some country",
}
},
"id": "1",
"name": "John Doe"
}
So no matter whether I embedded the resources or not I always know where they are located. And for the contacts collection I will have the link to my collection endpoint in the _links array and the contacts themselves in _embedded.
Yes. It's not only good practice to do so; it's recommended by the HAL spec:
Each Resource Object SHOULD contain a 'self' link that corresponds with the IANA registered 'self' relation (as defined by RFC5988) whose target is the resource's URI.
A collection of resources is itself a resource, don't forget.