Причина, по которой Json.NET создает колонку с типизированной строкой для "PhoneNo" : "123456789"
, состоит в том, что "123456789"
является, по сути, строковым литералом в соответствии с JSON standard. Числовой литерал будет выглядеть так, без двойных кавычек вокруг значения: 123456789
. Вы уверены, что эти свойства будут всегда быть числовыми строками? Не все телефонные номера являются числовыми, например, поэтому представляется неразумным, чтобы их жестко кодировать как таковые.
Это, как говорится, если вы уверены, что эти свойства будут всегда числовые строки и хотите Json.NET создавать числовые DataTable
столбцы для них, вы должны сказать ему заранее желаемого типа для этих столбцов. Один из вариантов - создать typed DataTable
из соответствующей схемы. В этом случае JsonConvert.DeserializeObject(Of TTypedDataTable)(myRecords)
создаст подкласс DataTable
с необходимыми типами столбцов.
Другой вариант - создать команду DataTable
вручную с соответствующим набором столбцов, а затем заполнить таблицу из вашего JSON. К сожалению, JsonConvert.PopulateObject()
не будет работать в предварительно размещенном DataTable
, поэтому вам нужно позвонить напрямую DataTableConverter.ReadJson()
. Это может быть сделано с помощью следующего метода расширения:
Public Module JsonExtensions
Public Sub PopulateDataTable(json As String, target As DataTable, Optional settings As JsonSerializerSettings = Nothing)
Using reader = New JsonTextReader(New StringReader(json))
Do
If reader.TokenType = JsonToken.StartArray Then
' Populate the table
Dim converter = New DataTableConverter()
converter.ReadJson(reader, target.GetType(), target, JsonSerializer.CreateDefault(settings))
End If
Loop While reader.Read()
End Using
End Sub
End Module
затем использовать его следующим образом:
Dim _dt = New DataTable()
_dt.Columns.Add("PhoneNo", GetType(Long))
_dt.Columns.Add("ID", GetType(Long))
JsonExtensions.PopulateDataTable(myRecords, _dt)
Пример fiddle.
Вы также написали, Я не могу жестко кодировать. Если вы действительно не знаете заранее, какие столбцы со строковыми значениями следует фактически десериализовать как числовые типы, то вы можете сделать предварительную обработку JSON, загрузив его в Jtoken
, группируя все значения свойств по имени и для каждая группа, проверяя, что все значениями в группе являются строки, которые могут быть конвертированы в числа. Если все конвертируемые, вы можете сделать преобразование. Но если только конвертируемые, вы не должны делать преобразование, так как это нарушит алгоритм вывода типа Json.NET. Это может быть сделано с помощью следующих методов расширения:
Public Module JsonExtensions
Private ReadOnly NumberTypes = New JTokenType() {JTokenType.[Integer], JTokenType.Float, JTokenType.[String], JTokenType.Comment, JTokenType.Raw, JTokenType.[Boolean]}
Private Function ValidateToken(o As JToken, validTypes As JTokenType(), nullable As Boolean) As Boolean
Return (Array.IndexOf(validTypes, o.Type) <> -1) OrElse (nullable AndAlso (o.Type = JTokenType.Null OrElse o.Type = JTokenType.Undefined))
End Function
<System.Runtime.CompilerServices.Extension> _
Public Function CanConvertToNullableLong(token As JToken) As Boolean
' Reverse engineered from
' public static explicit operator long?(JToken value)
' https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L1045
If token Is Nothing OrElse token.Type = JTokenType.Null OrElse token.Type = JTokenType.Boolean Then
Return True
End If
If Not ValidateToken(token, NumberTypes, True) Then
Return False
End If
Dim jValue = TryCast(token, JValue)
If jValue Is Nothing Then
Return False
End If
If TypeOf jValue.Value Is BigInteger Then
Dim i = CType(jValue.Value, BigInteger)
Return i <= Long.MaxValue AndAlso i >= Long.MinValue
End If
Dim s = CType(jValue, String)
Dim v As Long
Return Long.TryParse(s, NumberStyles.Number, NumberFormatInfo.InvariantInfo, v)
End Function
Public Sub TryConvertColumnsToNullableLong(root As JToken)
If TypeOf root Is JContainer Then
' If ALL columns values of a given name can be converted from string to long, then do so.
' Do not convert columns where some but not all are convertable.
For Each group In DirectCast(root, JContainer) _
.Descendants() _
.OfType(Of JProperty)() _
.GroupBy(Function(p) p.Name) _
.Where(Function(g) g.All(Function(p) (p.Value.Type = JTokenType.String Or p.Value.Type = JTokenType.Null) AndAlso p.Value.CanConvertToNullableLong()))
For Each p In group
p.Value = CType(p.Value, System.Nullable(Of Long))
Next
Next
End If
End Sub
End Module
Тогда предобработки и десериализации следующим образом:
Dim token = JToken.Parse(myRecords)
JsonExtensions.TryConvertColumnsToNullableLong(token)
Dim _dt = token.ToObject(Of DataTable)()
Пример fiddle #2.
Я не уверен, что рекомендовал бы это.Тот факт, что значения JSON являются строками, предполагает, что в базе данных, из которой был создан JSON, эти значения могут быть произвольными строк. Если это так, взлом в преобразовании в long
может вызвать проблемы позже, когда в базу данных начнут вводиться нечисловые значения.
Откуда берутся json? как это выглядит? Обычно номера телефонов * являются * строками: вы не можете их добавить или вычесть, чтобы они не были цифрами. Если идентификатор сериализуется как строка, вам, возможно, придется написать преобразователь, чтобы заставить его int – Plutonix