I have to deserialize some json data string into structure.
Problem is that data names conflicts vith VB keywords what is in C# not a case.
This is json string:
{"id":2526068,"date":"2019-07-21T19:15:17.4468196+02:00","error":""}
Problematic names are obviously "date" and "error". Somewhere I found that such variables should be surrended with []. But this don't work for me.
Here is my code:
Structure reqjson
Dim id As String
Dim [date] As String
Dim [error] As String
End Structure
Dim idnum As Long = 0
Dim sldate As String = ""
If Not String.IsNullOrEmpty(jsonstr) Then
Dim r As reqjson = JsonConvert.DeserializeObject(Of reqjson)(jsonstr)
idnum = CLng(r.id)
sladate = r.date.ToString("dd.MM.yyyy. hh:mm:ss.fff")
End If
Problem is that deserializer can't deserialize data if they don't have a same name what VB don't allow. In C# this declaration is legal:
struct reqjson{
string id;
string date;
string error;
};
But not in VB.NET. What to do here?
I don't see any problem with your deserialization. Your code works for me!
But perhaps you should address a couple potential issues. Don't use Dim for class level fields. Use Public or Private
Structure reqjson
Public id As String
Public [date] As String
Public [error] As String
End Structure
And I'm not changing anything here, other than adding the json string myself
Public Shared Sub foo()
Dim jsonstr = "{""id"":2526068,""Date"":""2019-07-21T19:15:17.4468196+02:00"",""error"":""""}"
Dim idnum As Long = 0
Dim sldate As String = ""
If Not String.IsNullOrEmpty(jsonstr) Then
Dim r As reqjson = JsonConvert.DeserializeObject(Of reqjson)(jsonstr)
idnum = CLng(r.id)
However, you are doing String.ToString(). Try this instead
sldate = Date.Parse(r.date).ToString("dd.MM.yyyy. hh:mm:ss.fff")
End If
End Sub
Or better yet, use an actual date in the struct
Structure reqjson
Public id As String
Public [date] As Date
Public [error] As String
End Structure
which makes your original code work
sldate = r.date.ToString("dd.MM.yyyy. hh:mm:ss.fff")
Related
All,
I need some help with understanding how classes can work with vb.NET and JSON.NET. I'm completely new to this. I've tried searching for answers, but I'm probably not asking the right questions. Here's my dilemma:
I have a JSON that I need to send to a REST API.
{
"paInfo":[
{
"providerAccountName":"someClient",
"providerAccountDescription":"A fine client.",
"providerName":"provider",
"externalProviderIdentifier":"BU4377890111"
},
{
"providerAccountName":"someClient1",
"providerAccountDescription":"A fine client.",
"providerName":"provider",
"externalProviderIdentifier":"BU4377890111"
}
],
"hubAccountName":"test"
}
I ran this through https://jsonutils.com/ to build my class as:
Public Class PaInfo
Public Property providerAccountName As String
Public Property providerAccountDescription As String
Public Property providerName As String
Public Property externalProviderIdentifier As String
End Class
Public Class addHubAcct
Public Property paInfo As PaInfo()
Public Property hubAccountName As String
End Class
From there, I'm trying to assign values to the class properties, but I don't quite understand how to pass the values for PaInfo to the property. Below is a snippet of code I'm using to assign values. If I try to assign a.paInfo = p, it errors:
error BC30311: Value of type 'PaInfo' cannot be converted to
'PaInfo()'
If I don't pass anything through to a.paInfo, I get a zero-length string in the JSON serialization.
Private Sub serializeAcct()
Dim p As New PaInfo
Dim a As New addHubAcct
p.providerAccountName = "Test\name'This ""that and the other'"
p.providerAccountDescription = "acct desc"
p.providerName = "tester"
p.externalProviderIdentifier = "123456"
a.hubAccountName = "Tester"
a.paInfo = p 'Here's my hangup
Dim o As String = JsonConvert.SerializeObject(a)
Dim deserializedProduct As addHubAcct = JsonConvert.DeserializeObject(Of addHubAcct)(o)
Stop
End Sub
?o.tostring,nq
{"paInfo":null,"hubAccountName":"Tester"}
Change the addHubAcct class like this:
Public Class addHubAcct
Public Property paInfo As New List(Of PaInfo)()
Public Property hubAccountName As String
End Class
And then change the bad line in serializeAcct() like this:
a.paInfo.Add(p)
You likely have other problems as well, but that should get you past the current obstacle.
Using List and .ToArray is what I was missing with my original code.
Private Sub serializeAcct()
Dim p1 As New PaInfo
Dim ps As New List(Of PaInfo)
Dim a As New addHubAcct
p1.providerAccountName = "Test\name'This ""that and the other'"
p1.providerAccountDescription = "acct desc"
p1.providerName = "tester"
p1.externalProviderIdentifier = "123456"
ps.Add(p1)
a.hubAccountName = "Tester"
a.paInfo = ps.ToArray
Dim o As String = JsonConvert.SerializeObject(a)
End Sub
I am writing this program where the output below is saved to a text file
Unresolved%Bob%NA%Smith%123%8
and opened up by a separate VB program. the text above is then made into a string
"Unresolved%Bob%NA%Smith%123%8" = String1
I now need to get each part separated by the % into it's own string. ex.
"Unresolved" = string1a
"Bob" = string1b
"NA" = string1c
and so on. So my question is, is this possible and how? or is there a better way?
With the String.Split method.
Dim input As String = "Unresolved%Bob%NA%Smith%123%8"
'make the string array
Dim output() As String = input.Split("%"c)
Now you have an array of all the parts.
Load them into memory - create an object to pars the data into:
Public Class Client
Public Property FirstName As String
Public Property MiddleName As String
Public Property LastName As String
Public Property Status As String
Public Property Value1 As String
Public Property Value2 As String
End Class
In method that parses the data:
Dim input As String = "Unresolved%Bob%NA%Smith%123%8"
'make the string array
Dim output() As String = input.Split("%"c)
Dim _client As New Client
_client.Status = output(0)
_client.FirstName = output(1)
_client.MiddleName = output(2)
_client.LastName = output(3)
_client.Value1 = output(4)
_client.Value2 = output(5)
After all that being said, you could parse lines from a file and add them into a collection if needed.
I have data in the following structure:
Structure student
Dim stdntpass As String
Dim fname As String
Dim sname As String
Dim age As Byte
Dim year As Integer
Dim stdntuser As String
End Structure
I need to take the data in that structure and output it to CSV. I was planning on using a StringBuilder object to do it, but I can't figure out how to give the structure to the StringBuilder.
Here is a function that uses reflection to determine what fields exist in the student structure:
Public Function StudentsToCSV(students As IEnumerable(Of student)) As String
Const separator As Char = ";"c
Dim sb As New StringBuilder
'Get the data elements
Dim studentFields = GetType(student).GetFields()
'Output headline (may be removed)
For Each field In studentFields
sb.Append(field.Name)
sb.Append(separator)
Next
sb.AppendLine("")
'Write a line for each student
For Each s In students
'Write the value of each field
For Each field In studentFields
Dim value As String = Convert.ToString(field.GetValue(s))
sb.Append(value)
'... followed by the separator
sb.Append(separator)
Next
sb.AppendLine("")
Next
Return sb.ToString()
End Function
You can pass any set of students to this function - may it be an array, a List(Of student) or whatever.
One way is to override the ToString function. now passing a whole object to a stringbuilder or even sending the Tostring function to the file will pass the values how you want them:
Structure student
Dim stdntpass As String
Dim fname As String
Dim sname As String
Dim age As Byte
Dim year As Integer
Dim stdntuser As String
Public Overrides Function ToString() As String
Return Join({stdntpass, fname, sname, age.ToString, year.ToString, stdntuser}, ","c) + vbNewLine
End Function
End Structure
The StringBuilder has no way to take a whole Structure and automatically append each of the properties from that Structure. If you must use a StringBuilder, you could do it like this:
builder.Append(s.stdntpass)
builder.Append(",")
builder.Append(s.fname)
builder.Append(",")
builder.Append(s.sname)
builder.Append(",")
builder.Append(s.age)
builder.Append(",")
builder.Append(s.year)
builder.Append(",")
builder.Append(s.stdntuser)
Dim csvLine As String = builder.ToString()
However, it would be easier to use the String.Join method, like this:
Dim csvLine As String = String.Join(",", s.stdntpass, s.fname, s.sname, s.age, s.year, s.stdntuser)
You could use reflection to dynamically get the list of properties from the structure, but then you won't have much control over the order in which the fields get appended without using attributes, or something, which could get ugly.
In any case, you should be careful, though, that values are properly escaped. If any of the strings in your structure contain commas, you need to surround that field with quotation marks. And if any of those strings quotation marks, they need to be doubled. You could fix the values with a method like this:
Public Function EscapeValueForCsv(value As String) As String
Return """" & value.Replace("""", """""") & """"
End Function
Then you could call that on each of the properties before passing it to String.Join:
Dim csvLine As String = String.Join _
(
",",
EscapeValueForCsv(s.stdntpass),
EscapeValueForCsv(s.fname),
EscapeValueForCsv(s.sname),
EscapeValueForCsv(s.age.ToString()),
EscapeValueForCsv(s.year.ToString()),
EscapeValueForCsv(s.stdntuser)
)
I want to refer to an enum by the string name of its root. Please note, i wish to refer to the enum, not an enum member.
There are many posts on stackoverflow describing how to refer an enum member by its name (eg
How to retrieve an Enum member given its name), but i didn't find any about how to refer to the enum by the name of it's root.
To further clarify;
Enum MyEnumA : Quiet : Noisy : End enum
Enum MyEnumB : Big : Small : Gigantic : End enum
Sub Foo(strAction as string)
' Depending on value of strAction, i want to create a list of either MyEnumA or MyEnumB members
' I know i can't do the following, it's just to make clear the direction i'm wanting to go -
Dim lstMembers As New List(Of CType(strAction,[Enum]))
'....
end function
Following the good suggestions below, i've tried this;
Dim enumType As Type = System.Type.GetType("ExcelInterface.clsBTAnalyseRslts+" & "strAction")
Dim lstFldIndx As New List(Of enumtype) 'Fails to compile this line as doesn't recognize enumtype as defined
Thank you!
Give this a go, it creates an array so you can change it to a List(Of x...) later:
Sub Foo(ByVal strAction As String)
Dim exAssembly = Reflection.Assembly.GetExecutingAssembly
Dim enumType = exAssembly.GetTypes.First(Function(f) f.Name = strAction)
Dim myEnum = [Enum].GetValues(enumType)
End Sub
I found an interesting post here which gave me some direction.
usage:
Foo("MyEnumA")
I will leave you to do some error handling and checking :D
Here's a code block that converts the enum into a string and then gets the type based on the string. It will show you how to get the type and how you should format the string. The rest should be pretty straight forward.
Dim obj As MyEnumA
Dim t As Type = obj.GetType()
Dim s As String = t.FullName
Dim t2 As Type = System.Type.GetType(s)
Then do this to get the values:
Dim Members() As String
Members = System.Enum.GetNames(t2)
Variable s will look something like this "namespace.class + MyEnumA" so all you need to do is to create this string programmatically and send it to a function.
Ric's answer didn't quite work for me because my Enum wasn't in the same location as my code, so here's how I finally managed it (I use VB, but I'll add C# for reference):
VB
Dim EnumName As String = "MyEnum"
Dim MyAssembly As System.Reflection.Assembly = System.Reflection.Assembly.GetAssembly(GetType(My.Assembly.Location))
Dim MyEnum As System.Type = MyAssembly.GetType("My.Assembly.Location+ClassName+" & EnumName)
Dim EnumNames As String() = MyEnum.GetEnumNames()
For intVal As Integer = 0 To EnumNames.Length - 1
' EnumNames(intVal) = Name of Enum Value
' intVal = Enum Value
Next
C#
string EnumName = "MyEnum";
System.Reflection.Assembly MyAssembly = System.Reflection.Assembly.GetAssembly(typeof(My.Assembly.Location));
System.Type MyEnum = MyAssembly.GetType("My.Assembly.Location+ClassName+" + EnumName);
string[] EnumNames = MyEnum.GetEnumNames();
for (int intVal = 0; intVal <= EnumNames.Length - 1; intVal++) {
// EnumNames(intVal) = Name of Enum Value
// intVal = Enum Value
}
Hopefully it will save someone's valuable time :).
I'm having problems deserializing some json data, getting InvalidCastExceptions and the like.
Can anyone point me in the right direction?
Here's the json i'm wanting to deserialize;
[{"OrderId":0,"Name":"Summary","MaxLen":"200"},{"OrderId":1,"Name":"Details","MaxLen":"0"}]
Here's my code;
Public Class jsTextArea
Public OrderId As Integer
Public Name As String
Public MaxLen As String
End Class
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim rawdata = js.DeserializeObject(textAreaJson)
Dim lstTextAreas As List(Of jsTextArea) = CType(rawdata, List(Of jsTextArea))
OrderId is an Int in your json (note the lack fo quotes round the values), but you're declaring it as String in "jsTextArea". Also, unless the type that rawdata is returned as has a cast to List(Of jsTextArea), which it probably doesn't the code you've shown won't work.
Update
To get the data out into a List(Of jsTextArea) try the following:
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim lstTextAreas = js.Deserialize(Of List(Of jsTextArea))(textAreaJson)
Doing it all on one line worked a treat;
Dim lstTextAreas As List(Of jsTextArea) = js.Deserialize(textAreaJson, GetType(List(Of jsTextArea)))
Dim textAreaJson As String = "[{""OrderId"":0,""Name"":""Summary"",""MaxLen"":""200""},{""OrderId"":1,""Name"":""Details"",""MaxLen"":""0""}]"
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim lstTextAreas As jsTextArea() = js.Deserialize(Of jsTextArea())(textAreaJson)
Here's a function to Deserialize JSON of any type:
Public Function DeserializeJson(Of T)(json As String) As T
Return New JavaScriptSerializer().Deserialize(Of T)(json)
End Function