Contentful. NET SDK Newtonsoft. Json .JsonReaderException Десериализация JSON в POCO - PullRequest
0 голосов
/ 09 июля 2020

Обновление 1 8. июля 2020 г. : Возможно, мне не стоило начинать с поля типа Rich Text, для доступа к которому, кажется, требуется довольно много дополнительной работы:

https://www.contentful.com/developers/docs/net/tutorials/rich-text/

Обновление 2 9. июля 2020 : неудивительно, что он не может сериализовать форматированный текст в строку. Посмотрите, как он хранит форматированный текст:

"fields": {"richTextField": {"nodeType": "document", "data": {}, "content": [{"nodeType": " абзац "," содержание ": [{" nodeType ":" текст "," значение ":" Это "," метки ": []," данные ": {}}, {" nodeType ":" текст " , "значение": "форматированный текст", "знаки": [{"тип": "подчеркивание"}], "данные": {}}, {"nodeType": "текст", "значение": "поле. "," mark ": []," data ": {}}]," data ": {}}]},

Это не выглядит забавным для повторной сборки в HTML. Если бы я хотел сломать его, я бы использовал HtmlAgilityPack.

Обновление 3 9. июля 2020:

На самом деле, это не имеет никакого смысла. Эта документация предполагает, что мои модели используют этот класс Contentful Document в моих POCO и что они должны содержать дополнительный код Contentful-specifici c для доступа к данным.

Одна из основных целей безголовой CMS состоит в том, чтобы избежать проблем со стороны поставщика. Speci c API (блокировка). Это работает следующим образом: CMS просто гидратирует POCO, в основном десериализуясь из JSON. Никакая другая безголовая CMS не будет заполнять такую ​​структуру из Rich Text, поэтому простое использование этого сумасшедшего формата для HTML приводит к привязке к поставщику или необходимости капитального ремонта кода только для изменения CMS (а не просто изменения logi гидратации POCO c, но с изменением самих POCO). Не говоря уже о полезной нагрузке JSON и нагрузке на ЦП от такого структурирования HTML. В любом случае, с архитектурой Contentful только для Rich Text Fields (вероятно, одним из самых распространенных элементов в CMS), существует множество кодов c, специфичных для поставщика, просто для получения значения, и интерфейс становится тоже осведомлен о CMS. Поэтому мне пришлось бы десериализовать какие-то промежуточные объекты, а затем использовать их для заполнения моих моделей представления. Или я мог бы скопировать все logi c вокруг Document в свой собственный проект, но это звучит почти хуже.

Я не понимаю, как кто-то может использовать эту систему. Пожалуйста, исправьте мои неправильные представления.

Исходная ветка

Я разместил это на https://www.contentfulcommunity.com/, но я не знаю, как получить это сообщение через модераторов.

Я стараюсь следовать этим инструкциям для Contentful. NET SDK для доступа к записи:

https://github.com/contentful/contentful.net

Возможно, я использую более новую версию. NET Core, но она не компилируется. Это компилирует:

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);
        }
    }
}

Но я получаю исключение при десериализации из JSON в POCO. Вот JSON для записи:

{
  "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": {}
        }
      ]
    }
  }
}

Это исключение:

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

1 Ответ

0 голосов
/ 09 июля 2020
    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;
        }

На самом деле это немного недетерминировано c, потому что несколько записей в нескольких типах контента могут иметь общий URL, и в этом случае это вернет один из них, но могут ли эти другие потоки переопределить послесловия результата, изменение данных, возвращаемых вызывающей стороне после того, как этот метод уже возвратил эти данные? Возможно, мне нужно явно создать потоки и отменить эти потоки при первом совпадении.

...