Extend DeserializeObject in Json.NET into a Try method - vb.net

I have been beating myself trying to figure out how to extend the generic method
Newtonsoft.Json.JsonConvert.DeserializeObject(Of T)(value As String)
I am trying to write a extension that would return Nothing if the deserialization failed. Ideally:
JsonConvert.TryDeserializeObject(Of T)(value As String)

I'm guessing that by extension that you did not mean an actual Extension, rather a way to extend Newtonsoft.JsonConvert.
An actual extension requires an instance object. JsonConvert is a Type with all shared/static members, so that wont work.
Nor can you extend the class by inheriting it because it is a sealed class (not inheritable).
The closest thing would be a string extension method:
<Extension>
Public Function JsonTryDeserialize(Of T)(json As String) As T
Dim obj As T
Try
obj = JsonConvert.DeserializeObject(Of T)(json)
Catch ex As Exception
Return Nothing
End Try
Return CType(obj, T)
End Function
Usage:
Dim jstr As String = ...json string from somewhere
Dim myFoo = jstr.JsonTryDeserialize(Of Foo)()
Personally, I think the best place for it is in the Type you are working with as a shared function; its more applicable, matches the other Json methods the code is simpler and Intellisence is not cluttered:
Class Foo
... stuff
Public Shared Function JsonTryDeserialize(json As String) As Foo
Dim f As Foo
Try
f = JsonConvert.DeserializeObject(Of Foo)(json)
Catch ex As Exception
Return Nothing
End Try
Return f
End Function
End Class
Usage:
newFoo = Foo.JsonTryDeserialize(strJ)

Related

Shared function that infers the class type and can be returned in a list

I would like to create a shared function that returns a list of instances of the classes type. Currently this is what my code looks like
class MyClass
Implements BusinessObject
Shared Function LoadAll(Of T As {BusinessObject, New})() As IEnumerable(Of T)
Dim helper = New SQLHelper()
Return helper.LoadDataTableFromDatabase("LoadTable", LoadAllProcedureName).Rows.Cast(Of DataRow).Select(Function(s) New T().FillDataRow(Of T)(s))
End Function
End Class
class MyDerivedClass Implements MyClass
End MyClass
When I go to use it, I have to use it like this:
MyDerivedClass.LoadAll(Of MyDerivedClass)()
I would like to be able to infer the type, instead of having to use the (Of MyDerivedClass) so that my code looks like MyDerivedClass.LoadAll().
Any help or keywords that I am missing to achieve this would be greatly appreciated.
Here is an extension method which (theoretically) would work on any class you define:
Public Module Module1
<Extension()> _
Public Function LoadAll(Of T As {BusinessObject, New})(ByVal x As T) As IEnumerable(Of T)
Dim LoadAllProcedureName As String = "LoadAllProcedure"
Dim helper = New SQLHelper()
Return helper.LoadDataTableFromDatabase("LoadTable", LoadAllProcedureName).Rows.Cast(Of DataRow).Select(Function(s) New T().FillDataRow(Of T)(s))
End Function
Public Sub Main()
Dim dC As New DerivedClass()
Dim allDc As IEnumerable(Of DerivedClass) = dC.LoadAll()
'::: Somewhat shorter syntax
Dim allDC As IEnumerable(Of DerivedClass) = (New DerivedClass()).LoadAll()
End Sub
End Module
But, as others have pointed out, this doesn't really clean anything up for you. More to the point, you are going to have to type (Of DerivedClass) in whatever variable you intend on populating with your enumerated DerivedClass, no?
And from what I can tell, you cannot have Shared extension methods -- should you be thinking that is the way to go.

How to mimic Java's Wildcard types in VB.net?

I have an interface which I defined like this:
Public Interface ISomething(Of T)
' methods
End Interface
I now did an implementation:
Public Class ConcreteThing
Implements ISomething(of SomeClass)
' Implementation
End Class
I have multiple such concrete implementations, and want to have a function which returns any of them based on its parameters. In Java, I would do something like this:
public ISomething<?> getSomething(ParamType p) {
if(p.hasFoo()) return new ConcreteThing();
if(p.hasBar()) return new OtherConcreteThing();
throw new IllegalStateException("p neither has Foo nor Bar");
}
I already searched about this issue and found out that VB.net does not have wildcard types, so I tried:
Public Function GetSomething(p as ParamType) as ISomething(Of Object)
If p.HasFoo Then Return New ConcreteThing()
If p.HasBar Then Return New OtherConcreteThing()
Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function
This compiles, but I get the warning: Runtime errors might occurr when converting 'Foo.ConcreteThing' to 'Foo.ISomething(Of Object)'.
When I try the following, as suggested in a similar question:
Public Function GetSomething(Of T)(p as ParamType) as ISomething(Of T)
If p.HasFoo Then Return New ConcreteThing()
If p.HasBar Then Return New OtherConcreteThing()
Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function
the warning only changes to Runtime errors might occurr when converting 'Foo.ConcreteThing' to 'Foo.ISomething(Of T)'.
So, how do I get this right? Or, if this indeed IS right, how do I have Visual Studio ignore this warning?
I investigated on this issue a little more, discussed it with my colleagues, and I think I found the solution / reason for the warnings.
The warning message is a bit hard to understand and unconcise. What they are trying to say is that, as silly as it sounds, covariance does not work as expected for primitive types, even when using the Out keyword!
Consider an excerpt from this example on MSDN:
' Covariance.
Dim strings As IEnumerable(Of String) = New List(Of String)()
' An object that is instantiated with a more derived type argument
' is assigned to an object instantiated with a less derived type argument.
' Assignment compatibility is preserved.
Dim objects As IEnumerable(Of Object) = strings
This works. Now, change the first IEnumerable to IList:
Dim strings As IList(Of String) = New List(Of String)()
Dim objects As IEnumerable(Of Object) = strings
Works, too. OK, we are lucky, let's change the second:
Dim strings As IList(Of String) = New List(Of String)()
Dim objects As IList(Of Object) = strings
Boom, InvalidCastException. Looking at the signature, this is because the generic parameter in IEnumerable is defined as Of Out T, and IList is only defined As T.
Now, let's define our own.
Interface ISomething(Of Out T)
ReadOnly Property Value As T
End Interface
Class IntThing
Implements ISomething(Of Integer)
Public ReadOnly Property Value As Integer Implements ISomething(Of Integer).Value
Get
Return 42
End Get
End Property
End Class
Now, do this:
Dim s1 As ISomething(Of Integer) = new IntThing()
Works. Now add this:
Dim s2 As ISomething(Of Object) = s1
Boom, InvalidCastException. Now, the funniest part. Add a second implementation of ISomething:
Class StringThing
Implements ISomething(Of String)
Public ReadOnly Property Value As String Implements ISomething(Of String).Value
Get
Return "foo"
End Get
End Property
End Class
And do:
Dim s1 As ISomething(Of String) = New StringThing()
Dim s2 As ISomething(Of Object) = s1
This, on the other hand, works! So, let's go back to the List example.
Dim ints As IEnumerable(Of Integer) = New List(Of Integer)()
Dim objects As IEnumerable(Of Object) = ints
This will get you an InvalidCastException, too.
So, my conclusion is that covariance not only needs the Out keyword, it additionally only works with non-primitive types. .net seems to handle wrapper classes differently to the JVM.
So, never ignore this warning when it pops up. When it does, things will go wonky in an absolutely illogical way! That means, for what I want to achieve, going with simple Objects instead trying to find an equivalent for ISomething<?> is the way to go.
I only use this internally to read a binary file into a more convenient structure to extract the data I pass out via the API in the end, so using Object does not make things very much worse here.
It's weird, I don't get the warning like you do. But I do get an InvalidCastException if I try to run the code.
To get rid of the error (and hopefully your warning as well), you can make the generic type T on ISomething covariant.
Public Interface ISomething(Of Out T) ' Add the "Out" keyword here to make it covariant
' methods
End Interface
Then you should be able to use your GetSomething function as you had attempted:
Public Function GetSomething(p as ParamType) as ISomething(Of Object)
If p.HasFoo Then Return New ConcreteThing()
If p.HasBar Then Return New OtherConcreteThing()
Throw New InvalidOperationException("p neither has Foo nor Bar")
End Function
Relevant documentation: Covariance and Contravariance in Generics
Covariance
Enables you to use a more specific type than originally specified.
You can assign an instance of IEnumerable<Derived> (IEnumerable(Of Derived) in Visual Basic) to a variable of type IEnumerable<Base>.
And lower in the Defining Variant Generic Interfaces and Delegates section:
A covariant type parameter is marked with the out keyword (Out keyword in Visual Basic, + for the MSIL Assembler).

vb.net: calling constructor when using generics

I'm not sure if this is possible or not.
I have a number of different classes that implement interface IBar, and have constructors that take a couple of values. Rather than create a bunch of almost identical method, is it possible to have a generic method that will create the appropriate constructor?
private function GetFoo(Of T)(byval p1, byval p2) as List(Of IBar)
dim list as new List(Of IBar)
dim foo as T
' a loop here for different values of x
foo = new T(x,p1)
list.Add(foo)
' end of loop
return list
end function
I get:
'New' cannot be used on a type parameter that does not have a 'New' constraint.
Unfortunately not - .NET generics only allow you to constrain a generic type to have a parameterless constructor, which you can then call with New T()... you can't specify a particular set of parameters.
If you don't mind making your types mutable, you could create an interface which containing a method with the relevant parameters, make all your types implement the interface, and then constrain the type to implement that method and have a parameterless constructor, but it's not ideal.
Another option is to pass in an appropriate Func which takes x and p1 and returns a new T each time. That would certainly be easy to use from C# - not quite so easy in VB IIRC, but worth considering nevertheless.
Expanding on Jon Skeet's answer, here's a possible solution using a Func parameter:
Private Function GetFoo(Of T As IBar)(ByVal p1 As Object, ByVal p2 As Object, ctor As Func(Of Integer, Object, T)) As List(Of IBar)
Dim list As New List(Of IBar)
Dim foo As T
For x = 1 To 10
foo = ctor(x, p1)
list.Add(foo)
Next
Return list
End Function
usage would be similar to
GetFoo(1, 2, Function(i, o) New BarImpl(i, o))
It is possible to cal, a constructor even if it is not specified in generic constraints. See the example below.
'This base class has no constructor except the default empty one
Public Class MyBaseClass
End Class
'this class inhetits MyBaseType, but it also implements a non empty constructor
Public Class MySpecializedClass
Inherits MyBaseClass
Public Sub New(argument As String)
End Sub
End Class
Public Function CreateObject(Of ClassType As MyBaseClass)(argument As String) As ClassType
'First, get the item type:
Dim itemType As Type = GetType(ClassType)
'Now we can use the desired constructor:
Dim constructor As ConstructorInfo = itemType.GetConstructor(New Type() {GetType(String)})
If constructor Is Nothing Then
Throw New InvalidConstraintException("Constructor ""New(String)"" not found.")
Else
Dim result As ClassType = constructor.Invoke(New Object() {argument})
Return result
End If
End Function
Public Sub RunTest()
Try
Console.WriteLine("+----------------------------------------------------+")
Console.WriteLine("Trying to create a instance of MyBaseClass")
Console.WriteLine("+----------------------------------------------------+")
Dim myobject As MyBaseClass = CreateObject(Of MyBaseClass)("string value")
Console.WriteLine(myobject)
Console.WriteLine("Instance of MyBaseClass created")
Catch ex As Exception
Console.WriteLine(ex)
End Try
Try
Console.WriteLine("+----------------------------------------------------+")
Console.WriteLine("Trying to create a instance of MySpecializedClass")
Console.WriteLine("+----------------------------------------------------+")
Dim myobject As MyBaseClass = CreateObject(Of MySpecializedClass)("string value")
Console.WriteLine(myobject)
Console.WriteLine("Instance of MySpecializedClass created")
Catch ex As Exception
Console.WriteLine(ex)
End Try
End Sub
Here is my answer.
Public CreateObject(Of T)() As T
Dim newObj = Activator.CreateInstance(GetType(T), YourParameterHere)
Return newObj
End Function
This will give you the new object. You can pass any parameters to this function.

How can I copy an object of an unknown type in VB.net?

Rather than giving the very specific case (which I did earlier), let me give a general example. Let's say that I have a function, called callingFunction. It has one parameter, called parameter. Parameter is of an unknown type. Let us then say that I wish to copy this parameter, and return it as a new object. For example, in pseudo code, something along the lines of...
Function callingFunction(ByVal parameter As Object) As Object
Dim newObj As New Object
'newObj has the same value as parameter, but is a distinctly different object
'with a different reference
newObj = parameter
return newObj
End Function
EDIT: Additional Information
The first time I posted this question, I received only one response - I felt that perhaps I made the question too specific. I guess I will explain more, perhaps that will help. I have an ASP page with 10 tables on it. I am trying, using the VB code behind, to come up with a single solution to add new rows to any table. When the user clicks a button, a generic "add row" function should be called.
The difficulty lies in the fact that I have no guarantee of the contents of any table. A new row will have the same contents as the row above it, but given that there are 10 tables, 1 row could contain any number of objects - text boxes, check boxes, etc. So I want to create a generic object, make it of the same type as the row above it, then add it to a new cell, then to a new row, then to the table.
I've tested it thoroughly, and the only part my code is failing on lies in this dynamic generation of an object type. Hence why I asked about copying objects. Neither of the solutions posted so far work correctly, by the way. Thank you for your help so far, perhaps this additional information will make it easier to provide advice?
You can't do this in general. And it won't be a good idea, for example, if parameter is of a type which implements the singleton pattern. If parameter is of a type which supports copying, it should implement the ICloneable interface. So, your function could look like this:
Function MyFunc(ByVal parameter As Object) As Object
Dim cloneableObject As ICloneable = TryCast(parameter, ICloneable)
If Not cloneableObject Is Nothing Then
Return cloneableObject.Clone()
Else
Return Nothing
End If
End Function
You could implement something like this:
Dim p1 As Person = New Person("Tim")
Dim p2 As Object = CloneObject(p1)
Dim sameRef As Boolean = p2 Is p1 'false'
Private Function CloneObject(ByVal o As Object) As Object
Dim retObject As Object
Try
Dim objType As Type = o.GetType
Dim properties() As Reflection.PropertyInfo = objType.GetProperties
retObject = objType.InvokeMember("", System.Reflection.BindingFlags.CreateInstance, Nothing, o, Nothing)
For Each propertyInfo As PropertyInfo In properties
If (propertyInfo.CanWrite) Then
propertyInfo.SetValue(retObject, propertyInfo.GetValue(o, Nothing), Nothing)
End If
Next
Catch ex As Exception
retObject = o
End Try
Return retObject
End Function
Class Person
Private _name As String
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal name As String)
Me.Name = name
End Sub
End Class
Here's a simple class that will work for most objects (assumes at least .Net 2.0):
Public Class ObjectCloner
Public Shared Function Clone(Of T)(ByVal obj As T) As T
Using buffer As MemoryStream = New MemoryStream
Dim formatter As New BinaryFormatter
formatter.Serialize(buffer, obj)
buffer.Position = 0
Return DirectCast(formatter.Deserialize(buffer), T)
End Using
End Function
End Class

Create a new instance of a type given as parameter

I've searched for an answer and found some c#-examples, but could not get this running in vb.net:
I thought of something like the following:
public function f(ByVal t as System.Type)
dim obj as t
dim a(2) as t
obj = new t
obj.someProperty = 1
a(0) = obj
obj = new t
obj.someProperty = 2
a(1) = obj
return a
End Function
I know, I can create a new instance with the Activator.Create... methods, but how to create an array of this type or just declare a new variable? (dim)
Thanks in advance!
It really depends on the type itself. If the type is a reference type and has an empty constructor (a constructor accepting zero arguments), the following code should create an insance of it:
Using Generics:
Public Function f(Of T)() As T
Dim tmp As T = GetType(T).GetConstructor(New System.Type() {}).Invoke(New Object() {})
Return tmp
End Function
Using a type parameter:
Public Function f(ByVal t As System.Type) As Object
Return t.GetConstructor(New System.Type() {}).Invoke(New Object() {})
End Function
Personaly I like this syntax much more.
Public Class Test(Of T As {New})
Public Shared Function GetInstance() As T
Return New T
End Function
End Class
Or if you want to limit the possible types:
Public Class Test(Of T As {New, MyObjectBase})
Public Shared Function GetInstance() As T
Return New T
End Function
End Class