1

I am using JsonTextWriter to construct a JSON string where the end result needs to look like this. The data property value "John Doe" will get replaced by a string variable to search for different names in a REST API call:

{
  "fields": [
    "name",
    "Company.name",
    "email",
    "mobile"
  ],
  "query": {
    "group": {
      "operator": "AND",
      "rules": [
        {
          "condition": "CONTAINS",
          "moduleName": "Contact",
          "field": {
            "fieldName": "name"
          },
          "data": "John Doe"
        }
      ]
    }
  }
}

This JsonTextWriter method is very hard for me to read, not intuitive. I'm guessing there is a way to instead create a class with all the properties and assign values? But I can't figure out how to deal with the nested stuff. Maybe better to use raw JSON for some parts to make it easier to make the code structure somewhat represent the final JSON? Here is the code I have now, it works, it is just clunky to read/edit. I'm thinking of something like LINQ to XML, where when you look at the LINQ code it is easy to "see" the XML structure :

Dim sb As StringBuilder = New StringBuilder()
Dim jw As JsonWriter = New JsonTextWriter(New StringWriter(sb))
jw.Formatting = Formatting.Indented
jw.WriteStartObject()

jw.WritePropertyName("fields")
jw.WriteStartArray()
jw.WriteValue("name")
jw.WriteValue("Company.name")
jw.WriteValue("email")
jw.WriteValue("mobile")
jw.WriteEndArray()

jw.WritePropertyName("query")
jw.WriteStartObject()
jw.WritePropertyName("group")
jw.WriteStartObject()
jw.WritePropertyName("operator")
jw.WriteValue("AND")
jw.WritePropertyName("rules")
jw.WriteStartArray()
jw.WriteStartObject()
jw.WritePropertyName("condition")
jw.WriteValue("CONTAINS")
jw.WritePropertyName("moduleName")
jw.WriteValue("Contact")
jw.WritePropertyName("field")
jw.WriteStartObject()
jw.WritePropertyName("fieldName")
jw.WriteValue("name")
jw.WriteEndObject()
jw.WritePropertyName("data")
jw.WriteValue("John Doe")
jw.WriteEndObject()
jw.WriteEndArray()
jw.WriteEndObject()
jw.WriteEndObject()
jw.WriteEndObject()

debug.writeline(sb.ToString)
dbc
  • 104,963
  • 20
  • 228
  • 340
Max Tyack
  • 21
  • 2
  • 5
  • Why not just create a vb.net data model from your JSON, then construct an instance and serialize it? https://jsonutils.com/ or [Paste JSON as Classes](https://stackoverflow.com/a/34274199) can create such a model automatically. – dbc Nov 30 '18 at 21:30
  • Wow, never knew VS had that built-in. I tried a web based utility and it was not producing a usable class. I think I'm almost there, just one snag... When I try to assign values to my "Fields" string array, VB says: Value of type String() cannot be converted to String. On this line: cSearch.fields = {"name", "Company.name", "email", "mobile"} Here is how that part of the class is declared: Public Class clContactSearchByName Public Property fields() As String Public Property query As Query End Class – Max Tyack Nov 30 '18 at 22:02

1 Answers1

0

You could define a hierarchy of anonymous objects corresponding to your JSON hierarchy using object initializer syntax, then serialize that:

Dim data As String = "John Doe"

Dim example = New With { _ 
    .fields = {"name","Company.name","email","mobile"}, _
    .query = New With { _
        .group = New With { _
            .[operator] = "AND", _
            .rules = { _
                New With { _
                    .condition = "CONTAINS", _
                    .moduleName = "Contact", _
                    .field = new With { .fieldName = "name" }, _
                    .data = data _
                } _
            } _
        } _
    } _                 
}

Dim json as String = JsonConvert.SerializeObject(example, Formatting.Indented)

Sample fiddle #1 here.

If you would prefer to use explicit, named types, you can use a code-generation tool such as https://jsonutils.com/ or Paste JSON as Classes to generate a data model corresponding to your JSON as follows:

Public Class Field
    Public Property fieldName As String
End Class

Public Class Rule
    Public Property condition As String
    Public Property moduleName As String
    Public Property field As Field
    Public Property data As String
End Class

Public Class Group
    ' Note I had to fix this by adding brackets around "operator", since it's a keyword.
    ' Public Property operator As String
    Public Property [operator] As String
    Public Property rules As Rule()
End Class

Public Class Query
    Public Property group As Group
End Class

Public Class Example
    Public Property fields As String()
    Public Property query As Query
End Class

Then allocate and serialize using the same object initializer syntax:

Dim data As String = "John Doe"

Dim example = New Example With { _ 
    .fields = {"name","Company.name","email","mobile"}, _
    .query = New Query With { _
        .group = New Group With { _
            .[operator] = "AND", _
            .rules = { _
                New Rule With { _
                    .condition = "CONTAINS", _
                    .moduleName = "Contact", _
                    .field = new Field With { .fieldName = "name" }, _
                    .data = data _
                } _
            } _
        } _
    } _                 
}

Dim json as String = JsonConvert.SerializeObject(example, Formatting.Indented)

Notes:

  • Operator is a vb.net keyword, so I had to add brackets around the auto-generated operator property.

  • If you are going to be building more complex examples of your data model, you might want to replace the auto-generated array properties with List(Of T) properties instead, e.g.

    Public Property rules As List(Of Rule)
    

Sample fiddle #2 here.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • This did the trick, thank you for your help. Not to take away from your answer or anything, but what gets me here is it seems silly that all those curly brackets are needed. I feel like the defined class' structure should take care of that automatically. Is there where the List of T might be helpful? – Max Tyack Nov 30 '18 at 22:23
  • `List(Of T)` might be helpful if you need to add fields or rules dynamically based on some conditions before serializing, since `List(Of T)` can be added to once allocated, while arrays cannot. – dbc Nov 30 '18 at 22:24
  • @MaxTyack - the curly brackets are required by the object initializer syntax. If you don't like them and you are using explicit types, you could allocate your objects line-by-line rather than all at one. I just prefer the object initializer syntax because it looks more modern and is required when working with immutable types. Also, the formatting of the object initializer syntax parallels the formatting of the JSON itself, making it easier to write and debug. – dbc Nov 30 '18 at 22:27