Сопоставьте результат Gremlin, содержащий строковые массивы, с классом C#, содержащим простые строковые свойства

Я столкнулся с проблемой при сопоставлении набора результатов Gremlin с классом на С#. Я пытаюсь получить вершины вместе со свойствами. Вот код:

public IList<T> All<T>() where T : class, new()
{
    Type type = typeof(T);
    string query = "g.V().hasLabel('" + type.Name.ToString().ToLower() + "').valueMap(true)";
    var resultSet = _gremlinClient.SubmitAsync<dynamic>(query).Result;
    List<T> list = JsonConvert.DeserializeObject<List<T>>(JsonConvert.SerializeObject(resultSet));
    return list;
}

А вот сущность пользователя, которую я передаю этому универсальному методу.

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

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

'При синтаксическом анализе значения обнаружен неожиданный символ: [. Путь '[0].FirstName', строка 1, позиция 37.'

Когда я проверил его, я обнаружил, что строка JSON имеет значения свойств с квадратными скобками, например:

[
  {
    "id": 0,
    "label": "user",
    "FirstName": [ "Azhar" ],
    "LastName": [ "Rahi" ]
  },
  {
    "id": 3,
    "label": "user",
    "FirstName": [ "John" ],
    "LastName": [ "Doe" ]
  }
]

Вы можете видеть, что некоторые свойства имеют квадратные скобки, такие как "FirstName":["Azhar"]. Я проверил его без квадратных скобок, и он работает нормально. Итак, причина ошибки в том, что строки представлены в виде массивов в JSON.

В приведенной выше строке JSON id и label являются автоматически сгенерированными Gremlin.Net свойствами. Остальные на самом деле являются User свойствами объекта. Я не знаю, почему Gremlin добавляет скобки при добавлении свойств к Vertex и есть ли возможность этого избежать.

Пожалуйста, предложите любое решение, либо в Gremlin.Net, либо каким-либо образом изменив сопоставление JSON с классом.


person azhar rahi    schedule 28.06.2020    source источник
comment
почему вы сериализуете и десериализуете одно и то же, возвращаете только resultSet   -  person Mohammed Sajid    schedule 28.06.2020
comment
@Gusman Я знаю это, но логически FirstName пользователя не может быть массивом. это заставит меня получить значение из индекса 0 массива. Во-вторых, я использую универсальный метод, поэтому решение должно быть универсальным. Я собираюсь обновить свое описание.   -  person azhar rahi    schedule 28.06.2020
comment
@Саджид, который выдал другие ошибки.   -  person azhar rahi    schedule 28.06.2020
comment
Итак, если я правильно понимаю, в resultSet у вас есть массив имени и фамилии, и вы сериализуете и десериализуете, чтобы избежать этого результата. Если да, вы можете использовать JsonConverter.   -  person Mohammed Sajid    schedule 28.06.2020
comment
Нет, @Sajid, я не хочу ничего избегать ... Я хочу сопоставить FirstName, LastName с объектом пользователя, т.е. нужно проанализировать json в объект пользователя (и установить значение null для свойств, которых нет в json) просто как это происходит в веб-API ASP.Net, когда вы отправляете данные в контроллер с помощью пользователя FormBody.   -  person azhar rahi    schedule 28.06.2020
comment
Связано: JSON C# десериализовать массив с 1 элементом в объект. См. SimplePropertyArrayToSingleConverter из этого ответа.   -  person dbc    schedule 29.06.2020


Ответы (1)


Итак, резюмируем: у вас есть некоторый JSON, для которого некоторые свойства являются массивами строк, но вы хотите смоделировать их в своих классах как отдельные строки, потому что массивы всегда должны иметь только одно значение.

Json.Net не будет обрабатывать это сопоставление по умолчанию, как вы видели. Но вы можете использовать пользовательский JsonConverter, чтобы заполнить пробел. Определите преобразователь следующим образом:

public class ArrayToSingleStringConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(string);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        switch (token.Type)
        {
            case JTokenType.Array:
                return (string)token.Children<JValue>().FirstOrDefault();
            case JTokenType.String:
                return (string)token;
            case JTokenType.Null:
                return null;
            default:
                throw new JsonException("Unexpected token type: " + token.Type);
        }
    }

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Затем для каждого строкового свойства в ваших классах, которое может быть массивом в JSON, добавьте атрибут [JsonConverter], указывающий ArrayToSingleStringConverter, например так:

public class User
{
    [JsonConverter(typeof(ArrayToSingleStringConverter))]
    public string FirstName { get; set; }

    [JsonConverter(typeof(ArrayToSingleStringConverter))]
    public string LastName { get; set; }
}

Когда вы десериализуете JSON, теперь он должен работать так, как вы хотите.
Демонстрационная скрипта: https://dotnetfiddle.net/Q9sGja

Примечание. Если вы хотите обрабатывать все свои строковые свойства таким образом, вы можете передать преобразователь в JsonConvert.DeserializeObject() вместо того, чтобы размечать свои классы атрибутами [JsonConverter]:

List<T> list = JsonConvert.DeserializeObject<List<T>>(json, new ArrayToSingleStringConverter());

Скрипт: https://dotnetfiddle.net/gcAqC5

person Brian Rogers    schedule 28.06.2020
comment
Хорошо, спасибо, Брайан. Вот я тоже думал. Я пойду на это. Но странно то, почему Gremlin устанавливает значения свойств с помощью квадратных скобок. Должно быть без скобок - person azhar rahi; 28.06.2020
comment
Я не знаю ответа на этот вопрос; Я не совсем знаком с Гремлином. Но я хорошо разбираюсь в Json.Net, поэтому предложил именно это решение. - person Brian Rogers; 28.06.2020
comment
Да, ваше решение довольно хорошее. Но я думаю, что мне нужно использовать Reflection, чтобы узнать, является ли свойство объекта User массивом или нет. Исходя из этого, мне нужно будет установить свойства объекта json в свойства объекта пользователя. - person azhar rahi; 28.06.2020
comment
Json.Net уже использует отражение; вот как это работает. Вам не нужно делать ничего сверх того, что я вам сказал. Либо пометьте строковые свойства в вашем классе с помощью атрибута преобразователя, либо передайте преобразователь в DeserializeObject. Преобразователь проверяет, является ли соответствующее свойство в JSON массивом или нет. Если это так, он берет первого ребенка; в противном случае он просто принимает его как строку. - person Brian Rogers; 28.06.2020
comment
Если вы говорите, что ваш класс имеет массивы строк (а не простые строки), а соответствующие свойства JSON иногда являются массивом, а иногда строкой, то это другая проблема и требуется другой преобразователь. См. Как обрабатывать как один элемент, так и массив для одного и того же свойства с помощью JSON.net. Но это не то, что вы описали в своем вопросе. - person Brian Rogers; 28.06.2020
comment
Если вы посмотрите на строку JSON в моем вопросе, вы обнаружите тот же случай, когда некоторые свойства являются строками, а некоторые — строковыми массивами. Те, которые являются простыми строками, автоматически создаются Gremlin (т. е. идентификатор и метка), а массивы строк добавляются пользовательским приложением с использованием операций Gremlin.Net. - person azhar rahi; 28.06.2020
comment
Как я уже сказал, я установил свойство вершины в соответствии с документацией, но Gremlin устанавливает предоставленные пользователем свойства в скобках (может быть, Gremlin рассматривает предоставленные пользователем данные как массивы). Я где-то читал, что это ожидаемое поведение Гремлина. Я все еще пытаюсь найти запрос Gremlin, который может получить значение в виде строки вместо массива (или без скобок, если оно имеет другое значение в Gremlin). - person azhar rahi; 28.06.2020
comment
Хорошо, я думаю, что мы на той же странице. Извините, меня смутил предыдущий комментарий. Этот ответ должен работать для вас. Попробуйте. - person Brian Rogers; 29.06.2020
comment
Хорошо, что сработало отлично. Спасибо. Однако, если я найду какое-либо простое решение вместо добавления атрибута к свойствам, я тоже поделюсь им. - person azhar rahi; 29.06.2020
comment
Без проблем; Я рад, что смог помочь. - person Brian Rogers; 29.06.2020