There is data in the form of json.
{
"type": "1C",
"counterparty": {
"name": "ИП Петя",
"inn": "123456789",
"kpp": ""
},
"nomenclatures": [
{
"cod": [{"type":"1C", "val":"РТ-1234"}, {"type":"Front", "val":"567"}],
"name": "Картоха",
"article": "123"
},
{
"cod": [{"type":"1C", "val":"РТ-1234"}, {"type":"Front", "val":"567"}],
"name": "Сникерс",
"article": "459"
}
]
}
There is a database.
enter image description here
3.Now I want to save this entity to database.
I saved three entities. There is one entity left that I cannot save. Help save the fourth entity Cod to the database.
#PostMapping(value = "/item")
public void postMain (#RequestBody NodeObject node) {
Counterparty counterparty = new Counterparty();
counterparty.setName(node.getCounterparty().getName());
counterparty.setInn(node.getCounterparty().getInn());
counterparty.setKpp(node.getCounterparty().getKpp());
counterpartyRepository.save(counterparty);
NodeObject nodeObject = new NodeObject();
nodeObject.setType(node.getType());
nodeObject.setId(counterparty.getId());
nodeObject.setCounterparty(counterparty);
nodeObject.setNomenclatures(node.getNomenclatures());
nodeObjectRepository.save(nodeObject);
Set<Nomenclature> nomenclatures = node.getNomenclatures();
nomenclatures.forEach(nomenclature -> nomenclature.setNodeObject(nodeObject));
nomenclatureRepository.saveAll(nomenclatures);
// ????????????????????????
List<Cod> cod = new ArrayList<>();
}
json comes to the controller. #RequestBody NodeObject node
Related
I am trying to build a pact between two services using asynchronous communication.
This is the code I used for generate the pact:
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "provider", providerType = ProviderType.ASYNCH)
public class StringifiedPactTest {
#Pact(consumer = "consumer", provider = "provider")
public MessagePact generatePact(MessagePactBuilder builder) {
return builder.hasPactWith("provider")
.expectsToReceive("A valid aws sns event")
.withContent(new PactDslJsonBody().stringType(new String[]{"MessageId", "TopicArn"}).stringValue("Message", new PactDslJsonBody().stringType("Value", "Foo").toString()))
.toPact();
}
#Test
#PactTestFor(pactMethod = "generatePact")
public void buildPact(List<Message> messages) {
}
}
And the generated pact is
{
"consumer": {
"name": "consumer"
},
"provider": {
"name": "provider"
},
"messages": [
{
"description": "A valid aws sns event",
"metaData": {
"contentType": "application/json"
},
"contents": {
"TopicArn": "string",
"Message": "{\"Value\":\"Foo\"}",
"MessageId": "string"
},
"matchingRules": {
"body": {
"$.MessageId": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.TopicArn": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "4.0.10"
}
}
}
This means the producer should have a "Message" that matches {"Value" : "Foo"}, any other combination like {"Value" : "Bar" } won't be successful.
Is there any way to add matching rules inside a stringified json?
Thanks!
Here's an anonymised example from a test we have. Hope it's useful. This creates a pact that matches only on type. So on the provider side, when I test against the contract, it doesn't matter what value I have for categoryName for example, as long as it's a stringType:
#PactTestFor(providerName = "provider-service", providerType = ProviderType.ASYNCH)
public class providerServiceConsumerPactTest {
private static String messageFromJson;
#BeforeAll
static void beforeAll() throws Exception {
messageFromJson = StreamUtils.copyToString(new ClassPathResource("/json/pact/consumer-service_provider-service.json").getInputStream(), Charset.defaultCharset());
}
#Pact(provider = "provider-service", consumer = "consumer-service")
public MessagePact providerServiceMessage(MessagePactBuilder builder) {
DslPart body = new PactDslJsonBody()
.object("metaData")
.stringType("origin", "provider-service")
.datetimeExpression("dateCreated", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.closeObject()
.minArrayLike("categories", 0, 1)
.stringType("id", "example data")
.stringType("categoryName", "example data")
.booleanType("clearance", false)
.closeObject()
.closeArray();
return builder
.expectsToReceive("a provider-service update")
.withContent(body)
.toPact();
}
#Test
#PactTestFor(pactMethod = "providerServiceMessage")
public void testProviderServiceMessage(MessagePact pact) {
// State
final String messageFromPact = pact.getMessages().get(0).contentsAsString();
// Assert
JSONAssert.assertEquals(messageFromPact, messageFromJson, false);
}
I'm having exactly the same issue, and unfortunately I don't think it's possible to tell Pact to parse the stringified JSON and look inside it (e.g. to verify that parse(Message).Value === "Foo" in your example).
The best you can do is write a regular expression to match the string you're expecting. This kind of sucks because there's no easy way to ignore the ordering of the JSON keys (e.g. "{\"a\":\"1\", \"b\":\"2\"}" and "{\"b\":\"2\", \"a\":\"1\"}" will compare different) but AFAIK Pact simply lacks the parsing functionality we're looking for, so the only tool it provides is regex.
Update 1 8.July.2020: Maybe I should not have started with the Rich Text field type, which seems to take quite a bit of additional work to access:
https://www.contentful.com/developers/docs/net/tutorials/rich-text/
Update 2 9.July.2020: No wonder it cannot serialize Rich Text to a string. Look how it stores the Rich Text:
"fields": {
"richTextField": {
"nodeType": "document",
"data": {},
"content": [
{
"nodeType": "paragraph",
"content": [
{
"nodeType": "text",
"value": "This is the ",
"marks": [],
"data": {}
},
{
"nodeType": "text",
"value": "rich text",
"marks": [
{
"type": "underline"
}
],
"data": {}
},
{
"nodeType": "text",
"value": " field.",
"marks": [],
"data": {}
}
],
"data": {}
}
]
},
This doesn't look fun to reassemble into HTML. If I wanted it broken down, I would use HtmlAgilityPack.
Update 3 9.July.2020:
Actually, this doesn't make any sense. This documentation suggests that my models use this Contentful Document class in my POCOs and that they should contain additional Contentful-specific code for accessing data.
One of the main goals of headless CMS is to avoid vendor-specific APIs (lock-in). The way that works is that the CMS just hydrates POCOs, basically deserializing from JSON. No other headless CMS is going to populate a structure like this from Rich Text, so just using this crazy format for HTML leads to vendor-lock-in or the need for a major code overhaul just to change the CMS (not just changing the POCO hydration logic, but changing the POCOs themselves). Not to mention the JSON payload and CPU load impact of structuring HTML like this. In any case, with the Contentful architecture just for Rich Text Fields (probably one of the most common elements in a CMS), there is a bunch of vendor-specific coding just to get the value and the front-end becomes way too aware of the CMS. So I would have to deserialize to some kind of intermediary objects and then use those to populate my view models. Or I could replicate all of the logic around Document into my own project, but that sounds almost worse.
I don't see how anyone can use this system. Please correct my misperceptions.
Original thread
I posted this on https://www.contentfulcommunity.com/, but I don't know how to get that post through the moderators.
I try to follow these instructions for the Contentful .NET SDK to access an Entry:
https://github.com/contentful/contentful.net
I may be using newer .NET Core, but it doesn’t compile that way. This compiles:
using Contentful.Core;
using Contentful.Core.Configuration;
using System;
using System.Net.Http;
namespace CfClt
{
class Program
{
public class Entry
{
public string richTextField { get; set; }
}
static void Main(string[] args)
{
HttpClient httpClient = new HttpClient();
ContentfulOptions options = new ContentfulOptions
{
DeliveryApiKey = "A",
PreviewApiKey = "B",
SpaceId = "C"
};
ContentfulClient client = new ContentfulClient(httpClient, options);
Entry entry = client.GetEntry<Entry>("4SVaB1ps4H6Ml9ZxWsCWOn").GetAwaiter().GetResult();
Console.WriteLine(entry.richTextField);
}
}
}
But I get an exception deserializing from JSON to the POCO. Here is the JSON for the Entry:
{
"sys": {
"space": {
"sys": {
"type": "Link",
"linkType": "Space",
"id": "qjiunow8a0ig"
}
},
"id": "4SVaB1ps4H6Ml9ZxWsCWOn",
"type": "Entry",
"createdAt": "2020-07-08T19:52:47.34Z",
"updatedAt": "2020-07-08T19:52:47.34Z",
"environment": {
"sys": {
"id": "master",
"type": "Link",
"linkType": "Environment"
}
},
"revision": 1,
"contentType": {
"sys": {
"type": "Link",
"linkType": "ContentType",
"id": "firstContentType"
}
},
"locale": "en-US"
},
"fields": {
"richTextField": {
"nodeType": "document",
"data": {},
"content": [
{
"nodeType": "paragraph",
"content": [
{
"nodeType": "text",
"value": "this is the rich text field. What is my ID?",
"marks": [],
"data": {}
}
],
"data": {}
}
]
}
}
}
This is the exception:
Unhandled exception. Newtonsoft.Json.JsonReaderException: Error reading string. Unexpected token: StartObject. Path 'fields.richTextField', line 32, position 22.
at Newtonsoft.Json.JsonReader.ReadAsString()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.Linq.JToken.ToObject(Type objectType, JsonSerializer jsonSerializer)
at Newtonsoft.Json.Linq.JToken.ToObject[T](JsonSerializer jsonSerializer)
at Contentful.Core.ContentfulClient.GetEntry[T](String entryId, String queryString, CancellationToken cancellationToken) in C:\temp\cfclt\CfClt\Contentful.Core\ContentfulClient.cs:line 136
at CfClt.Program.Main(String[] args) in c:\temp\cfclt\CfClt\CfClt\Program.cs:line 26
using Contentful.Core;
using Contentful.Core.Configuration;
using Contentful.Core.Models;
using Deliverystack.Core.Fulcontent.Models.Repositories;
using System;
using System.Net.Http;
namespace cfclt
{
class Program
{
public class Entry
{
public string StringField { get; set; }
public string Url { get; set; }
public Document RichTextField { get; set; }
}
static void Main(string[] args)
{
HttpClient httpClient = new HttpClient();
ContentfulOptions options = new ContentfulOptions
{
DeliveryApiKey = "A",
PreviewApiKey = "B",
SpaceId = "C"
};
ContentfulClient client = new ContentfulClient(httpClient, options);
ContentfulRepository repository = new ContentfulRepository(client);
Entry entry = repository.Get<Entry>("/");
Console.WriteLine(entry + " : " + entry.StringField + " : " + entry.Url + " : " + new HtmlRenderer().ToHtml(entry.RichTextField).GetAwaiter().GetResult());
}
}
}
using Contentful.Core;
using Contentful.Core.Models;
using Contentful.Core.Search;
using Deliverystack.Core.Models.Repositories;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Deliverystack.Core.Fulcontent.Models.Repositories
{
public class ContentfulRepository : IRepository
{
private ContentfulClient _client;
public ContentfulRepository(ContentfulClient client)
{
_client = client;
}
public override TEntryModel Get<TEntryModel>(string entryIdentifier, string contentTypeUid = null)
{
TEntryModel result = null;
if (!entryIdentifier.StartsWith("/"))
{
result = _client.GetEntry<TEntryModel>(entryIdentifier).GetAwaiter().GetResult();
}
else
{
List<string> contentTypes = new List<string>();
if (contentTypeUid != null)
{
contentTypes.Add(contentTypeUid);
}
else
{
foreach (ContentType doNotUseCt in _client.GetContentTypes().GetAwaiter().GetResult())
{
ContentType myContentType = doNotUseCt;
contentTypes.Add(myContentType.SystemProperties.Id);
}
}
foreach(string doNotUseCti in contentTypes)
{
string myContentTypeUid = doNotUseCti;
QueryBuilder<TEntryModel> queryBuilder = QueryBuilder<TEntryModel>.New.ContentTypeIs(myContentTypeUid).FieldEquals("fields.url", entryIdentifier);
ContentfulCollection<TEntryModel> entries = _client.GetEntries(queryBuilder).GetAwaiter().GetResult();
if (entries.Count() > 0)
{
result = entries.Items.First();
}
}
}
return result;
}
Actually this is a bit non-deterministic because multiple entries in multiple content types could have a common URL, in which case this would return one of them, but would those other threads possibly override result afterwords, changing the data returned to the caller after this method has already returned that data? Maybe I need to create threads explicitly and cancel those threads on the first match.
I got a null object attributes after deserialization of a json response.
Developing under android, I'm using retrofit2 , moshi as converter (https://github.com/kamikat/moshi-jsonapi ) .
When debugging ,I saw a json response fully retrieved (not null attributes),but deserialization fails. Should I use GSON instead?
Here's my retrofit builder I use to make my json call: (no issue)
public static JsonServerInterface getSimpleClient(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_AUTH_URL)a
.addConverterFactory(MoshiConverterFactory.create())
.build();
JsonServerInterface webServer=retrofit.create(JsonServerInterface.class);
return webServer;
}
My api json call,response contain UserModel with null attributes(deserialization fails without any error)
signInCall.enqueue(new Callback<UserModel>(){
#Override
public void onResponse
(Call<UserModel> call, Response<UserModel> response)
{
response.message();
}
}
My UserModel (as required by moshi ,but I think it lacks something):
#JsonApi(type = "users")
public class UserModel extends Resource {
#Json(name = "auth-token")
private String authToken;
#Json(name = "firstname")
private String firstname;
#Json(name = "lastname")
private String lastname;
#Json(name = "email")
private String email;
#Json(name = "created-at")
private String createdAt;
#Json(name = "updated-at")
private String updatedAt;
private HasMany<ActivityModel> activities;
My json response I saw when debugging http response, I retrieve without any trouve,but moshi sucks to deserialize it,and no errors are raised:
{
"data": {
"id": "21",
"type": "users",
"attributes": {
"auth-token": "t8S3BTqyPwN3T4QDMY1FwEMF",
"firstname": "aymen",
"lastname": "myself",
"email": "aymen.myself#gmail.com",
"created-at": "2017-11-13T22:52:39.477Z",
"updated-at": "2017-11-13T23:21:09.706Z"
},
"relationships": {
"activities": {
"data": [
{
"id": "81",
"type": "activities"
}
]
}
}
},
"included": [
{
"id": "81",
"type": "activities",
"attributes": {
"title": "activity 10",
"description": "how to draw a circle",
"start-at": "2017-11-13T23:06:13.474Z",
"duration": 10,
"created-at": "2017-11-13T23:06:32.630Z",
"updated-at": "2017-11-13T23:06:32.630Z"
},
"relationships": {
"user": {
"data": {
"id": "21",
"type": "users"
}
}
}
}
]
}
I find the solution after lot of hours:
I should use "Document" instead of UserModel
interface:
#POST("sign-in.json")
Call<Document> signIn(#Body Credentials credentials);
when calling:
signInCall.enqueue(new Callback<Document>(){
#Override
public void onResponse(Call<Document> call, Response<Document> response) {
hope it helps
I am using swagger ui plugin to document my web api. I want to return JSON object like:
{"Person": {
"Id": 1,
"Name": "John",
"address": {
"Street": "ABC",
"City": "Penrith",
"PostCode": 2034,
"State": "NSW"
},
"DOB": "2013-11-11T00:00:00"
}
}
Notice the Person object name.
I can do this with the following code:
public HttpResponseMessage Get(int id)
{
Person person = new Person { Id = 1, Name = "John", DOB = new DateTime(2013, 11, 11), address = new Address { City = "Penrith", State = "NSW", PostCode = 2034, Street = "ABC" } } ;
return Request.CreateResponse(HttpStatusCode.Accepted, new { Person = person });
}
Unfortunately, because the return type is HttpResponseMessage and not Person itself, Swagger just shows model as HttResponseMessage. That's not what I want.
If I change the return type to Person and return a person object, I don't get the the Person object name in the JSON return. That returns only -
{
"Id": 1,
"Name": "John",
"address": {
"Street": "ABC",
"City": "Penrith",
"PostCode": 2034,
"State": "NSW"
},
"DOB": "2013-11-11T00:00:00"
}
Is there a way to return Person but have the JSON string with Person object name?
Simply create a new class which has a Person property, instance it, assign the value to the Person property and return this object.
In this way, the JSON will look as expected.
If you don't want to create a new class, try using an anonymous type, like this:
// If you have this object
var MyPerson = ...;
// Return this from Web API
return new { Person = MyPersons };
(I don't know if this last option will work for you)
The response of my service ALFRESCO REST is:
[
{
"role": "SiteManager",
"authority":
{
"authorityType": "USER",
"fullName": "admin",
"userName": "admin",
"firstName": "Administrator",
"lastName": "",
"url": "\/alfresco\/service\/api\/people\/admin"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/admin"
}
,
{
"role": "SiteConsumer",
"authority":
{
"authorityType": "GROUP",
"shortName": "jamalgg",
"fullName": "GROUP_jamalgg",
"displayName": "jamalgg",
"url": "\/alfresco\/service\/api\/groups\/jamalgg"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/GROUP_jamalgg"
}
,
{
"role": "SiteManager",
"authority":
{
"authorityType": "GROUP",
"shortName": "ALFRESCO_ADMINISTRATORS",
"fullName": "GROUP_ALFRESCO_ADMINISTRATORS",
"displayName": "ALFRESCO_ADMINISTRATORS",
"url": "\/alfresco\/service\/api\/groups\/ALFRESCO_ADMINISTRATORS"
},
"url": "\/alfresco\/service\/api\/sites\/test3\/memberships\/GROUP_ALFRESCO_ADMINISTRATORS"
}
]
And I want to parse to list of object:
List<Memberships > listMemberships;
public class Memberships {
private String role;
private List<Authority> listAuthority ;
private String url;
}
public class Authority {
private String authorityType;
private String shortName;
private String fullName;
private String displayName;
private String url;
}
I think that there are two solutions:
how to add the tag Memberships to JSON result for encapsulates
the whole.
how to parse JSON result directly to my list
Thanks
As answered in a-better-java-json-library I would use the google-gson library.
Thank you Ozoli. The answer to my question is:
Type targetType = new TypeToken<Collection<Memberships>>() {}.getType();
List<Memberships> list = (List<Memberships>) new Gson().fromJson(renduJson,targetType);
You can also use http://jsongen.byingtondesign.com/ to generate java code from json response and then use jackson library ( http://jackson.codehaus.org/ ) to bind that response data to your object(s):
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("c:\\user.json"), User.class);
sorry for not formatting code
Type targetType = new TypeToken<Collection<Memberships>>() {}.getType();
List<Memberships> list = (List<Memberships>)new Gson().fromJson(rendu,targetType);