Есть задача, в которой нужно будет сериализовывать и десериализовывать entity или коллекцию. Стандартное обращение к JsonConvert.SerializeObject не работает сходу, но можно настроить с помощью пользовательских объектов, чтобы отбрасывались лишние свойства. Но DeserializeObject сделать так и не получилось. В доках нашёл функционал в ядре системы, предназначенный для этой задачи. Получился примерно такой код:

        public virtual string Serialize(Entity entity)
        {
            using (var textWriter = new StringWriter())
            using (var writer = GetDataWriter(textWriter))
            {
                entity.WriteData(writer);
                return textWriter.ToString();
            }
        }
 
        public virtual Entity Deserialize(string schemaName, string stringValue)
        {
            var manager = UserConnection.EntitySchemaManager;
            var entitySchema = manager.GetInstanceByName(schemaName);
            var entity = entitySchema.CreateEntity(UserConnection);
            using (var textReader = new StringReader(stringValue))
            using (var reader = GetReader(textReader))
            {
                entity.ReadData(reader);
                return entity;
            }
        }
 
        protected override DataWriter GetDataWriter(TextWriter textWriter)
        {
            var settings = new JsonDataWriterSettings()
            {
                WriteDefValues = false,
                QuoteName = false
            };
            var writer = new JsonDataWriter(settings, textWriter);
            return writer;
        }
 
        protected override DataReader GetReader(TextReader textReader)
        {
            var reader = new JsonDataReader(textReader);
            return reader;
        }

Аналогичный код для EntityCollection. 

Сериализация в json таким образом работает. Но немного странно. В результирующей json-строке есть открывающая фигурная скобка, но нет закрывающей. 

Десериализация в json не работает вообще. ReadData отрабатывает без исключений, но на выходе получается пустой экземпляр без заполненных колонок. Аналогичная проблема с EntityCollection. Только сама коллекция пустая.

Сериализация в xml оказалась вообще неработоспособной. 

У кого-то получалось заставить этот функционал работать? Или может кто нашёл какой-то другой вариант решения задачи?

Нравится

2 комментария
Лучший ответ

Насколько понимаю, для Entity не используют DeserializeObject, в коде конфигурации его используют для фильтров и подобного. Например, для работы с данными в пакете есть свои функции в PackageSchemaDataUtilities: Deserialize и Serialize. Примеры из тестов, проверяющих работу с ними:

   EntitySchema schema = CreateSchema("TestSchema", userConnection.EntitySchemaManager);
   AddIntegerColumn(schema, "ColumnInt");
   AddTextColumn(schema, "ColumnText");
   AddDateTimeColumn(schema, "ColumnDateTime");
   PackageSchemaDataDescriptor dataDescriptor = CreateTestPackageSchemaData(schema.UId, schema.Name);
   foreach (var column in schema.Columns) {
    dataDescriptor.AddColumn(new PackageSchemaDataColumnDescriptor(column.UId, false));
   }
   var testDateTime2012 = DateTime.Parse("2012-01-01 12:00");
   TimeSpan testUtcOffset2012 = TimeZoneInfo.Local.GetUtcOffset(testDateTime2012);
   var testDateTime2013 = DateTime.Parse("2013-01-01 00:00");
   TimeSpan testUtcOffset2013 = TimeZoneInfo.Local.GetUtcOffset(testDateTime2013);
   string columnIdUId = schema.Columns.GetByName("Id").UId.ToString();
   string columnIntUId = schema.Columns.GetByName("ColumnInt").UId.ToString();
   string columnTextUId = schema.Columns.GetByName("ColumnText").UId.ToString();
   string columnDateTimeUId = schema.Columns.GetByName("ColumnDateTime").UId.ToString();
   string data = @"{
    ""PackageData"": [
     {
      ""Row"": [
       {
        ""SchemaColumnUId"": """ + columnIdUId + @""",
        ""Value"": ""f16d852d-d896-4792-99ff-f36fdd0ab2e4""
       },
       {
        ""SchemaColumnUId"": """ + columnIntUId + @""",
        ""Value"": 1
       },
       {
        ""SchemaColumnUId"": """ + columnTextUId + @""",
        ""Value"": ""test 1""
       },
       {
        ""SchemaColumnUId"": """ + columnDateTimeUId + @""",
        ""Value"": ""\/" + testDateTime2012.ToJsonFormat(testUtcOffset2012) + @"\/""
       }
      ]
     },
     {
      ""Row"": [
       {
        ""SchemaColumnUId"": """ + columnIdUId + @""",
        ""Value"": ""8b022a83-796a-426c-8337-b6fd40525a49""
       },
       {
        ""SchemaColumnUId"": """ + columnIntUId + @""",
        ""Value"": 2
       },
       {
        ""SchemaColumnUId"": """ + columnTextUId + @""",
        ""Value"": ""test 2""
       },
       {
        ""SchemaColumnUId"": """ + columnDateTimeUId + @""",
        ""Value"": ""\/" + testDateTime2013.ToJsonFormat(testUtcOffset2013) + @"\/""
       }
      ]
     }
    ]
   }";
   var dataUtilities = new PackageSchemaDataUtilities(userConnection);
   ICollection<Entity> entities = dataUtilities.Deserialize(data.ToStream(), schema);

И обратное:

 EntitySchema schema = CreateSchema("TestSchema", userConnection.EntitySchemaManager, false);
 EntitySchemaColumn columnText = AddTextColumn(schema, "ColumnText");
 ICollection<Entity> entities = new Collection<Entity>();
 var entity = schema.CreateEntity(userConnection);
 entity.SetColumnValue(columnText, "Column Text Value");
 entities.Add(entity);
 string data;
 var dataUtilities = new PackageSchemaDataUtilities(userConnection);
 dataUtilities.SerializeColumnName = true;
 using (var dataStream = new MemoryStream()) {
 	dataUtilities.Serialize(dataStream, entities, schema.Columns.Select(c => c.UId));
 	dataStream.Position = 0;
 	data = StreamUtilities.GetStreamContent(dataStream);
 }

Чтобы работать с ними, используется:

using Terrasoft.Core.Packages;

 

Насколько понимаю, для Entity не используют DeserializeObject, в коде конфигурации его используют для фильтров и подобного. Например, для работы с данными в пакете есть свои функции в PackageSchemaDataUtilities: Deserialize и Serialize. Примеры из тестов, проверяющих работу с ними:

   EntitySchema schema = CreateSchema("TestSchema", userConnection.EntitySchemaManager);
   AddIntegerColumn(schema, "ColumnInt");
   AddTextColumn(schema, "ColumnText");
   AddDateTimeColumn(schema, "ColumnDateTime");
   PackageSchemaDataDescriptor dataDescriptor = CreateTestPackageSchemaData(schema.UId, schema.Name);
   foreach (var column in schema.Columns) {
    dataDescriptor.AddColumn(new PackageSchemaDataColumnDescriptor(column.UId, false));
   }
   var testDateTime2012 = DateTime.Parse("2012-01-01 12:00");
   TimeSpan testUtcOffset2012 = TimeZoneInfo.Local.GetUtcOffset(testDateTime2012);
   var testDateTime2013 = DateTime.Parse("2013-01-01 00:00");
   TimeSpan testUtcOffset2013 = TimeZoneInfo.Local.GetUtcOffset(testDateTime2013);
   string columnIdUId = schema.Columns.GetByName("Id").UId.ToString();
   string columnIntUId = schema.Columns.GetByName("ColumnInt").UId.ToString();
   string columnTextUId = schema.Columns.GetByName("ColumnText").UId.ToString();
   string columnDateTimeUId = schema.Columns.GetByName("ColumnDateTime").UId.ToString();
   string data = @"{
    ""PackageData"": [
     {
      ""Row"": [
       {
        ""SchemaColumnUId"": """ + columnIdUId + @""",
        ""Value"": ""f16d852d-d896-4792-99ff-f36fdd0ab2e4""
       },
       {
        ""SchemaColumnUId"": """ + columnIntUId + @""",
        ""Value"": 1
       },
       {
        ""SchemaColumnUId"": """ + columnTextUId + @""",
        ""Value"": ""test 1""
       },
       {
        ""SchemaColumnUId"": """ + columnDateTimeUId + @""",
        ""Value"": ""\/" + testDateTime2012.ToJsonFormat(testUtcOffset2012) + @"\/""
       }
      ]
     },
     {
      ""Row"": [
       {
        ""SchemaColumnUId"": """ + columnIdUId + @""",
        ""Value"": ""8b022a83-796a-426c-8337-b6fd40525a49""
       },
       {
        ""SchemaColumnUId"": """ + columnIntUId + @""",
        ""Value"": 2
       },
       {
        ""SchemaColumnUId"": """ + columnTextUId + @""",
        ""Value"": ""test 2""
       },
       {
        ""SchemaColumnUId"": """ + columnDateTimeUId + @""",
        ""Value"": ""\/" + testDateTime2013.ToJsonFormat(testUtcOffset2013) + @"\/""
       }
      ]
     }
    ]
   }";
   var dataUtilities = new PackageSchemaDataUtilities(userConnection);
   ICollection<Entity> entities = dataUtilities.Deserialize(data.ToStream(), schema);

И обратное:

 EntitySchema schema = CreateSchema("TestSchema", userConnection.EntitySchemaManager, false);
 EntitySchemaColumn columnText = AddTextColumn(schema, "ColumnText");
 ICollection<Entity> entities = new Collection<Entity>();
 var entity = schema.CreateEntity(userConnection);
 entity.SetColumnValue(columnText, "Column Text Value");
 entities.Add(entity);
 string data;
 var dataUtilities = new PackageSchemaDataUtilities(userConnection);
 dataUtilities.SerializeColumnName = true;
 using (var dataStream = new MemoryStream()) {
 	dataUtilities.Serialize(dataStream, entities, schema.Columns.Select(c => c.UId));
 	dataStream.Position = 0;
 	data = StreamUtilities.GetStreamContent(dataStream);
 }

Чтобы работать с ними, используется:

using Terrasoft.Core.Packages;

 

Зверев Александр,

Благодарю! Такой способ оказался рабочим. 

Показать все комментарии

Добрый день!

Не подскажет ли кто-нибудь ответ на такой вопрос: можно ли "перехватить" json до его скармливания системе на десериализацию с тем, чтобы преобразовать его в массив и исключить ряд элементов?

суть проблемы:
сайт отдает json с информацией о заказе услуги. В этом json есть довольно много данных, которые не нужны в CRM, но они нужны в случае, если через CRM будет производиться редактирование заказа (в этой ситуации срмка должна будет вернуть сайту тот же самый json с частью обновленных данных).

Отсюда и возник вопрос - можно ли, до десериализации, "ненужные" данные вычленить из json (сохранив оригинал отдельно) и скормить системе только то, что нужно для работы?

Кроме преобразования в массив я ничего не могу придумать, а bpm не умеет работать с json как с текстом (только как с объектом... или нет?)

Спасибо и сорри, если немного невнятно :)

Нравится

1 комментарий

JSON - это строка. Делайте что хотите с ней :)

Показать все комментарии

Здравствуйте.
У меня два вопроса.
1. Где можно посмотреть внятную помощь по вопросу Сериализации/десериализации данных в террасофте? На форуме не нашел. В файле с SDK - только пара слов: "Serialize/deserialize() - метод сохранения/восстановления объекта". А как пользоваться - не описано.

2. Как следствие, у меня возникает ошибка при попытке десериализации:

var XMLStorage = GetNewXMLStorage();
XMLStorage.InitRootNode('FiltersBuilderRootNode');
fbcFilters.FiltersBuilder.RootItems.Serialize(XMLStorage.RootNode);
FilterText = XMLStorage.Text; // тут все нормально

// теперь десериализуем. Пишем всё тоже, что и выше только теперь:
// А вот тут вдруг ни с того ни с сего ошибка "parse Error!":
XMLStorage.Text = FilterText;

Почему? Как это исправить?

Нравится

4 комментария

Здравствуйте.

Хороший пример реализации хранения данных в профиле можно посмотреть в скрипте wnd_PlanningViewPeriodTreeScript в функциях SavePeriodTreeToProfile() и LoadPeriodTreeFromProfile().

Спасибо, Дмитрий, но у меня, к сожалению, такого скрипта нету. Версия софта - 3.2.0.87

Здравствуйте.

Скопирую сюда:

function SavePeriodTreeToProfile() {
	if (!PlanningViewPeriodTree.Planning) {
		return;
	}
 
	var XMLStorage = GetNewXMLStorage();
	var Node;
	var Today = new Date;
	var Data;
 
	XMLStorage.Clear();
	XMLStorage.InitRootNode('PeriodTree');
	Node = XMLStorage.RootNode;
	Node.SetAttributeAsInt('BeginYear', PlanningViewPeriodTree.BeginYear, 
		Today.getFullYear());
	Node.SetAttributeAsInt('EndYear', PlanningViewPeriodTree.EndYear, 
		Today.getFullYear());
	Node.SetAttributeAsStr('Period', PlanningViewPeriodTree.Planning.Period, 
		'');
	Node.SetAttributeAsBool('IsRefreshManually', cbRefreshManually.IsChecked, 
		false);
 
	Data = DatasetToMime(dlData.Dataset);
	Node.SetAttributeAsStr('Dataset', Data, '');
 
	var Key = PlanningViewPeriodTree.Planning.ID;
	Key = '_PeriodTree_' + Key.replace(/[{}-]/g, '');
	Services.SaveItemProfileStorage(Self.USI, Key, XMLStorage);
}
function LoadPeriodTreeFromProfile() {
	if (!PlanningViewPeriodTree.Planning) {
		return false;
	}
 
	var XMLStorage;
	var Node;
	var Today = new Date;
	var Data;
 
	var Key = PlanningViewPeriodTree.Planning.ID;
	Key = '_PeriodTree_' + Key.replace(/[{}-]/g, '');
	XMLStorage = Services.GetItemProfileStorage(Self.USI, Key);
	if (!XMLStorage) {
		return false;
	}
	XMLStorage.InitRootNode('PeriodTree');
	Node = XMLStorage.RootNode;
	PlanningViewPeriodTree.BeginYear = Node.GetAttributeAsInt('BeginYear', 
		Today.getYear());
	PlanningViewPeriodTree.EndYear = Node.GetAttributeAsInt('EndYear', 
		Today.getYear());
	edtFromYear.Value = PlanningViewPeriodTree.BeginYear;
	edtToYear.Value = PlanningViewPeriodTree.EndYear;
	cbRefreshManually.IsChecked = Node.GetAttributeAsBool('IsRefreshManually', 
		false);
 
	var Period = Node.GetAttributeAsStr('Period', '');
	if (IsEmptyGUID(Period) || 
		(Period != PlanningViewPeriodTree.Planning.Period)) {
		return false;
	}
	Data = Node.GetAttributeAsStr('Dataset', '');
	if (Data == '') {
		return false;
	}
 
	var Dataset = dlData.Dataset;
	if (!MimeToDataset(Dataset, Data)) {
		return false;
	}
	Dataset.Attributes('Filled') = true;
	ExpandTree();
	return true;
}

Спасибо ещё раз. Посмотрел. Не всё, конечно, понятно, но разобрался с тем, что хотел :)

Показать все комментарии