YamlDotNet - Serialization of Guid properties generates empty brackets - vb.net

I am using YamlDotNet library to serialize some objects in Yaml. I've met some problems with the serialization of Guid properties.
Serialization of Guid properties generates empty brackets ( ex: {} )
See code below
Dim l As New List(Of Person)
l.Add(New Person() With {.Firstname = "MyFirstName", .Lastname = "MyLastName", .Id = Guid.NewGuid()})
Using sw As New StreamWriter("output.yaml", False)
Dim serializer = New Serializer()
serializer.Serialize(sw, l)
End Using
this code will output :
- Id: {}
Firstname: MyFirstName
Lastname: MyLastName
With the class:
Public Class Person
Public Property Id As Guid
Public Property Frstname As String
Public Property Lastname As String
End Class
Am I missing something or is this an issue of the library ?

You can define a custom converter to use when you need to customize the serialization of a type. The converter needs to implement IYamlTypeConverter, and be registered on the Serializer or Deserializer. Here is an example of such a converter:
Public Class GuidConverter
Implements IYamlTypeConverter
Public Function Accepts(type As Type) As Boolean Implements IYamlTypeConverter.Accepts
Return type = GetType(Guid)
End Function
Public Function ReadYaml(parser As IParser, type As Type) As Object Implements IYamlTypeConverter.ReadYaml
Dim reader = New EventReader(parser)
Dim scalar = reader.Expect(Of Scalar)()
Return Guid.Parse(scalar.Value)
End Function
Public Sub WriteYaml(emitter As IEmitter, value As Object, type As Type) Implements IYamlTypeConverter.WriteYaml
emitter.Emit(New Scalar(value.ToString()))
End Sub
End Class
The usage is quite simple:
Dim serializer = New Serializer()
serializer.RegisterTypeConverter(New GuidConverter())
serializer.Serialize(Console.Out, New With {.id = Guid.NewGuid()})
You can see a fully working example here.

Related

json object to VB.Net array / Dictionary

I have been tasked with adding a REST server to our company to work with an outside vendor. I have been able to complete most of it using VB.Net. However I am not sure how to handle the example below where there are 3 descriptors (OrderNumber,Desination,ItemNumbers) and ItemNumbers is an array. There may be any number of ItemNumbers in a single post. For the others I have made a class with properties, how can I do this with an array? I am using Newtonsoft.Json. Please post an example and Thank You!
Public Class Quality 'Example of how I have done the other json Posts
Private OrderNumberValue As String
Public Property OrderNumber() As String
Get
Return OrderNumberValue
End Get
Set(ByVal value As String)
OrderNumberValue = value
End Set
End Property
Private DestinationValue As String
Public Property Destination() As String
Get
Return DestinationValue
End Get
Set(ByVal value As String)
DestinationValue = value
End Set
End Property
END CLASS
This is the example json being posted:
{
"OrderNumber": "SO-1234567890",
"Destination": "Dock1 / Floor / whatever",
"ItemNumbers": [
{
"ItemNumber": "400100",
"CasesRequired": "140"
},
{
"ItemNumber": "400200",
"CasesRequired": "140"
}
]
}
you can use Newtonsoft.Json
Imports Newtonsoft.Json
Dim order As Order = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Order)(json)
and you need two classes
Public Class ItemNumber
Public Property ItemNumber As String
Public Property CasesRequired As String
End Class
Public Class Order
Public Property OrderNumber As String
Public Property Destination As String
Public Property ItemNumbers As ItemNumber()
End Class

vb.net Dictionary(Of String, Object) enum.getname()

I am doing an VB.Net WinForm Application. It is a migration of C#.
In C# I have a variable defined like this.
private static Dictionary<string, ExportFormatType> dicExtensiones =
new Dictionary<string, ExportFormatType> {
{".pdf", ExportFormatType.PortableDocFormat},
{".doc", ExportFormatType.WordForWindows},
{".xls", ExportFormatType.Excel},
{".rtf", ExportFormatType.RichText},
{".html", ExportFormatType.HTML40},
{".txt", ExportFormatType.Text}
};
And i migrated to this..
Private Shared dicExtensiones = New Dictionary(Of String, ExportFormatType) From
{{".pdf", ExportFormatType.PortableDocFormat},
{".doc", ExportFormatType.WordForWindows},
{".xls", ExportFormatType.Excel},
{".rtf", ExportFormatType.RichText},
{".html", ExportFormatType.HTML40},
{".txt", ExportFormatType.Text}}
Now I need to Loop through all ítems and get each value...
In C# is like this.
List<String> lista = new List<string>();
foreach (var item in dicExtensiones)
{
lista.Add(Enum.GetName(typeof(ExportFormatType), item.Value));
lista.Add("*" + item.Key);
}
The problem i have is that I do know how to migrate
Enum.GetName(typeof(ExportFormatType), item.Value);
to VB.Net, because Enum.GetName does not exists in VB.Net
How can I do it?
it can be like below in VB
Dim lista As List(Of [String]) = New List(Of String)()
For Each item As var In dicExtensiones
lista.Add([Enum].GetName(GetType(ExportFormatType), item.Value))
lista.Add("*" + item.Key)
Next
In VB, Enum is a keyword, as well as a class name, so you need to escape it in your code. The escaping syntax is similar to SQL:
[Enum].GetName
By escaping it, you're telling the compiler that you are referring to the identifier by that name rather than the keyword. For instance, you might also need to escape your own class or variable names on occasion:
Dim [property] As String = "belt, wallet with $50, casio watch"
or
Public Class [Class]
Public Property Teacher As String
Public Property Students As List(Of Student)
End Class
Though, in most cases it's preferable to just avoid it by thinking of a different name to use.

How should I serialize a vb.NET object which has class attributes with JsonConvert?

I am trying to use Json.NET to serialize an object in vb.NET.
The object fails to serialize correctly. This seems to be because of some attributes on the class.
Below is the class definition – a very simple definition.
Here is the code to serialize:
Dim MyObject As New TestClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Serialized As String = JsonConvert.SerializeObject(MyObject)
After the 2 lines above execute, the variable Serialized has the following value (which is not what I would expect or want):
"Namespace1.TestClass"
When I remove the class attributes completely (just the class attributes, not the property attributes) and then execute the same 2 lines of code, the variable Serialized has the following value (which is what I would expect):
{"Property1":"Hello","Property2":"3"}
This is only an example: we have many such classes with these kinds of attributes. We need to serialize them with Json.NET.
Removing the attributes is not possible, the classes I am dealing with are part of a system of existing applications and WCF based web services (i.e. part of our system needs our current serialization system for WCF kept in place, and another part of our system needs to serialize the same class with Json .. I won't go into the "why" details, just that we are serializing thousands of such objects for database writes and have speed and space isues).
I realize also that I can use JsonTextWriter to serialize, but then we have maintenance issues – every time we add/remove a property we have to remember to maintain the serialization code appropriately.
So how must I serialize this class correctly without removing the attributes?
I have not seen anything on the NewtonSoft site, nor anywhere else, which addresses this specific problem.
Here again is the class definition, along with the attributes.
<System.CodeDom.Compiler.GeneratedCodeAttribute ("System.Xml", "2.0.50727.3053"), _
System.SerializableAttribute(), _
System.Diagnostics.DebuggerStepThroughAttribute(), _
System.ComponentModel.DesignerCategoryAttribute("code"), _
System.Xml.Serialization.XmlTypeAttribute ([Namespace]:="http://Namespace.com/SomePath/SomeXsd.xsd", TypeName:="TestClass"), _
System.ComponentModel.TypeConverterAttribute(GetType (System.ComponentModel.ExpandableObjectConverter))> _
Partial Public Class TestClass
Private _Property1 As String
Private _Property2 As Integer
<System.ComponentModel.Browsable(False), System.Xml.Serialization.XmlIgnoreAttribute()> _
Public Property Property1() As String
Get
Return Me._Property1
End Get
Set(ByVal value As String)
If (Me._Property1 <> value) Then
Me._Property1 = value
End If
End Set
End Property
<System.ComponentModel.Browsable(False), System.Xml.Serialization.XmlIgnoreAttribute()> _
Public Property Property2() As String
Get
Return Me._Property2
End Get
Set(ByVal value As String)
If (Me._Property2 <> value) Then
Me._Property2 = value
End If
End Set
End Property
End Class
The problem is the TypeConverterAttribute on your class. When Json.Net sees that, it will use the associated TypeConverter to convert the object to a string. In this case, it results in the class's type name being output.
You can override the unwanted behavior by adding a JsonObjectAttribute to the classes that have a TypeConverterAttribute applied. But since it appears that your classes are generated code, it might not be feasible to do that on an class-by-class basis unless you can modify the code generator. In that case, another alternative is to use a custom IContractResolver to force Json.Net to ignore the TypeConverter on classes that have it.
Here is the code you would need for the resolver:
Class TypeConverterIgnoringResolver
Inherits DefaultContractResolver
Protected Overrides Function CreateContract(objectType As Type) As JsonContract
If objectType.GetCustomAttributes(True) _
.OfType(Of System.ComponentModel.TypeConverterAttribute)() _
.Any() _
Then
Return MyBase.CreateObjectContract(objectType)
End If
Return MyBase.CreateContract(objectType)
End Function
End Class
You can use the resolver like this:
Dim MyObject As New TestClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Settings As New JsonSerializerSettings
Settings.ContractResolver = New TypeConverterIgnoringResolver()
Dim Serialized As String = JsonConvert.SerializeObject(MyObject, Settings)
Console.WriteLine(Serialized)
Fiddle: https://dotnetfiddle.net/s6Ebmc
I guess you don't want to change your TestClass as it must have been generated by some tool, I would suggest to derive a new class from it:
Imports Newtonsoft.Json
<JsonObject()>
Public Class OtherClass
Inherits TestClass
End Class
and use the attribute <JsonObject()>. This should do the trick:
Dim MyObject As New OtherClass() With {.Property1 = "Hello", .Property2 = 3}
Dim Serialized As String = JsonConvert.SerializeObject(MyObject)
UPDATE:
Since you're already working with a Partial you can extend it creating a new one (in another folder):
Imports Newtonsoft.Json
<JsonObject()>
Partial Public Class TestClass
End Class

Using Reflection to Get All Static Properties in a Class As Objects VB.NET

I would like to start that I don't want a to hear about how expensive and terrible reflection is. That won't help—I have a very good reason to use reflection and that's not my question.
Specifically, I have a class within a class that contains several static properties of the same type.
Public Class Foo
Public Class Bar
Public Shared Property prop1 As New CustomClass()
Public Shared Property prop2 As New CustomClass()
Public Shared Property prop3 As New CustomClass()
End Class
End Class
Public Class CustomClass
Public Sub DoStuff()
End Sub
End Class
I'm looking to create a method in Foo that calls DoStuff on each of the properties contained within it. How can I do this? Here's the general idea of what I want to include in Foo, but I obviously can't convert PropertyInfo to CustomClass:
Private Sub Example()
For Each prop As PropertyInfo In GetType(Foo.Bar).GetProperties()
DirectCast(prop, CustomClass).DoStuff()
Next
End Sub
How can I get the static properties and cast them to CustomClass objects?
PropertyInfo represents the type's property get/set method pair. To evaluate the getter you simply call GetValue, like so:
(in C# because I'm a language snob)
foreach( PropertyInfo pi in typeof(Foo.Bar).GetProperties() ) {
// Use null as arguments because it's a static property without an indexer.
Object got = pi.GetValue( null, null );
CustomClass got2 = got as CustomClass;
if( got2 != null ) {
Console.WriteLine( got2.ToString() );
}
}
And to convert Dai's answer to VB because I'm not a language snob:
For Each pi As System.Reflection.PropertyInfo in Foo.Bar.GetType.GetProperties()
' Use nothing as arguments because it's a shared property without an indexer.
Dim got = pi.GetValue(Nothing, Nothing)
Dim got2 as CustomClass = DirectCast(got, CustomClass)
If Not IsNothing(got2) Then Console.WriteLine(got2.toString())
Next
huzzah for less lines and more keystrokes...

How can I access a class variable via an array in VB.NET?

If I have the following class and declaration:
Public Class objLocation
Public SysLocationId As String
Public NameFull As String
Public LatRaw As String
Public LongRaw As String
Public Active As Integer
End Class
dim lLocation as new objLocation
I can access each variable thus lLocation.SysLocationId, etc. Is there an alternate way, so I can access each variable by index, so something like lLocation(0), lLocation(1), etc., which gives me the flexibility to compare to classes of the same type via a for next loop, or against other sources, like a datatable.
If your goal is comparison, usually what you'll do is implement the IComparable interface or overload the >, < operators (if an ordering is needed) or just the = operator (if equivalence is needed).
You just write one function in one location and invoke that function whenever you need to do your comparison. The same goes for comparing to objects stored in a database. Where you put these functions depends on your application architecture, but for the object-object comparison you can have it as part of the objLocation class itself.
There is no built-in langauge support for this. However you can simulate this by creating a default indexer property on the class
Public Class objLocation
...
Default Public ReadOnly Property Indexer(ByVal index As Integer)
Get
Select Case index
Case 0
Return SysLocationId
Case 1
Return NameFull
Case 2
Return LatRaw
Case 3
Return LongRaw
Case 4
Return Active
Case Else
Throw New ArgumentException
End Select
End Get
End Property
Then you can use it as follows
Dim x As objLocation = GetObjLocation
Dim latRaw = x(2)
No, you can not do this outright.
You have to use reflection to get the properties, but you have to be aware that there is no guarantee on the order of the properties returned (which is important if you want to index them numerically).
Because of that, you will have to keep the sort order consistent when working with the properties (and indexes).
Are you looking for a List:
Dim LocationList As List<objLocation>;
For Each loc As objLocation In LocationList
loc.whatever
Next
or to use the index:
For i = 0 To LocationList.Length - 1
LocationList(i).whatever
Next
sorry, if the VB syntax isn't right...I've been doing C# lately and no VB
You can do that as follows. It is C# and something is a bit different with using indexers in VB, but you should absolutly be able to get it working in VB.
public class ObjLocation
{
private String[] Properties = new String[5];
public const Int32 IndexSysLocationId = 0;
public const Int32 IndexNameFull = 1;
public const Int32 IndexLatRaw = 2;
public const Int32 IndexLongRaw = 3;
public const Int32 IndexActive = 4;
// Repeat this for all properties
public String SysLocationId
{
get { return this.Properties[ObjLocation.IndexSysLocationId]; }
set { this.Properties[ObjLocation.IndexSysLocationId] = value; }
}
public String this[Int32 index]
{
get { return this.Properties[index]; }
set { this.Properties[index] = value; }
}
}
Now you have the object with the properties as before, but stored in an array and you can also access them through an indexer.
This method I implemented in a public structure to return an array of string variables stored in a structure:
Public Shared Function returnArrayValues() As ArrayList
Dim arrayOutput As New ArrayList()
Dim objInstance As New LibertyPIMVaultDefaultCategories()
Dim t As Type = objInstance.GetType()
Dim arrayfinfo() As System.Reflection.FieldInfo = t.GetFields()
For Each finfo As System.Reflection.FieldInfo In arrayfinfo
Dim str As String = finfo.GetValue(objInstance)
arrayOutput.Add(str)
Next
Return arrayOutput
End Function
Put it inside the structure or a class. Maybe this sample code helps.