1

I'm new to JSON.Net and haven't been able to figure out how to get a nested object of my JSON as a JObject. I am using a file to store my JSON objects, and then write the changes I made back to the file.

Background

I am trying to make an app where the user selects a folder on their computer, and the app enumerates that folder and all of its files into a JSON object. The user can then select any of those files and set values of that file to be saved to the JSON file. So far I have made the function to generate the directory structure/contained files into JSON and save it to a file, but I am stuck at trying to then access a nested object of the JSON in order to set a property's value.

My code so far

The code used to generate the JSON and store it into the file is:

Private Sub btnAddFolder_Click(sender As Object, e As EventArgs) Handles btnAddFolder.Click

    'Create JObject from JSON File
    Dim json = File.ReadAllText(jsonFile)
    Dim obj As New JObject
    If json <> "" Then
        obj = JObject.Parse(json)
    End If

    'Add directory as a nested object of JSON
    ' - Add all of files in directory as nested objects of the directory object
    If obj.Property(txtDirectory.Text) Is Nothing Then
        'Object to store all files as nested objects
        Dim files As New JObject
        'Object to store all properties of a file object
        Dim fileInfo As New JObject
        'Variables to store properties for fileInfo
        Dim stringProp As String = "default"
        Dim arrProp() As String = {"default"}
        Dim jArrProp As JArray = JArray.FromObject(arrProp)

        'Add properties to objcet
        fileInfo.Add("var1", stringProp)
        fileInfo.Add("var2", jArrProp)

        'Add object of properties to each file object
        For Each file As String In Directory.GetFiles(txtDirectory.Text)
            files.Add(file, fileInfo)
        Next

        'Add each file object to directory object
        obj.Add(txtDirectory.Text, files)
    End If

    'Write JSON to file
    File.WriteAllText(jsonFile, JsonConvert.SerializeObject(obj, 1))
End Sub

The generated file/JSON looks like:

{
  "Directory1": {
    "File1": {
      "var1": "default",
      "var2": ["default"]
    },
    "File2": {
      "var1": "default",
      "var2": ["default"]
    }
  },
  "Directory2": {
    "File1": {
      "var1": "default",
      "var2": ["default"]
    },
    "File2": {
      "var1": "default",
      "var2": ["default"]
    }
  }
}

What I need next

The code above is called when a Button labeled Add Folder is clicked.
Another Button, labeled Select File and Set Data, prompts Users to select any file from the directory and input values that will be stored in var1 and var2 of that file.

For example, when the Select File and Set Data Button is clicked, a User selects File1, then writes "This file is a picture from my 2012 vacation to the islands." in a TextBox. I need my code to set var1 of obj.Directory1.File1 to the string value specified.
This is where I am stumped. I do not know how to use JSON.net to access a nested object in order to set a value of one of its properties.

I know that a JObject has a .Property().Value() method that can be used to set or get the value of a property, but since my intended JSON object is nested inside the main JSON object, I cannot figure out how to get it as a JObject in order to access that method. JObject.Item() returns a JToken which does not have a .Property().Value() method.

0

1 Answer 1

1

Your JSON structure contains Directories names or full paths, which are all different, so a class model is not really useful to describe the whole content of this JSON.
Since a Directory path is unique (you cannot have two identical paths that refer to distinct Directory roots), the Directory path can be used as the Key of a Dictionary.
It's also simple to determine whether the Dictionary already contains that Key (since you're checking this in your code), using the Dictionary.ContainsKey("Key") method.

The same applies to the Files names: usually a Directory cannot contain files with identical names. So you can use the File Path as the Key of a second Dictionary, which is the Value of the first one (which, as described, has the Directory as Key).

The var1 and var2 values are instead always the same, so you can use a class model to describe this structure. You can also assign default values to the properties (in different ways; here I'm just assigning a default value explicitly)

Public Class FileObj
    <JsonProperty("var1")>
    Public Property Var1 As String = "default"
    <JsonProperty("var2")>
    Public Property Var2 As List(Of String) = New List(Of String)({"default"})
End Class

At this point, when you have deserialize your JSON, you just check whether the Dictionary already contains a Key equal to the new Directory path.
If it does not, you add this new Key and a Dictionary(Of String, FileObj) as Value, where the Key is the File Name and FileObj is the class that contains your two properties:

Load the already saved content, if any:

Private DirectoryObjects As Dictionary(Of String, Dictionary(Of String, FileObj))

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim json = File.ReadAllText(jsonFile)
    DirectoryObjects = JsonConvert.DeserializeObject(
        Of Dictionary(Of String, Dictionary(Of String, FileObj)))(json)
End Sub

Add a new Directory structure to the current Dictionary:

Private Sub btnAddFolder_Click(sender As Object, e As EventArgs) Handles btnAddFolder.Click
    Dim dirPath = txtDirectory.Text
    If DirectoryObjects.ContainsKey(dirPath) Then Return

    Dim fileInfo As New Dictionary(Of String, FileObj)()
    For Each file As String In Directory.GetFiles(dirPath)
        fileInfo.Add(file, New FileObj() With {
            .Var1 = "Something",
            .Var2 = New List(Of String)({"Something", "Else"})
        })
    Next

    DirectoryObjects.Add(dirPath, fileInfo)

    ' Now you can serialize and write to disc to preserve the changes
    Dim dirInfoJson = JsonConvert.SerializeObject(DirectoryObjects)
    File.WriteAllText(jsonFile, dirInfoJson)
End Sub

Now, you can use, e.g., a ComboBox - to select one of the Directories - and a ListBox / ListView - to show the content of the selected Directory.
When one File is selected, the Directory is the Key of the outer Dictionary, the File the Key of the inner Dictionary.
► A Dictionary.ToList() can be used as the DataSource of most Controls that hold collection of items.

  • txtDescription.Text is the meant to represent the User input related to var1
  • txtDataVar2a.Text and txtDataVar2b.Text other inputs that represent the values of var2.
Private Sub btnEditFileInfo_Click(sender As Object, e As EventArgs) Handles btnEditFileInfo.Click
    ' Get the inner Dictionary using the Directory as the Key of the outer Dictionary
    Dim fileObjects As Dictionary(Of String, FileObj) = DirectoryObjects(txtDirectory.Text)
    ' Get the FileObj object using the File as the Key of the inner Dictionary
    Dim fileInfo As FileObj = fileObjects("[Selected File Path]")
    ' Add the new Values to var1 and var2
    fileInfo.Var1 = txtDescription.Text
    fileInfo.Var2 = New List(Of String)({txtDataVar2a.Text, txtDataVar2b.Text})

    ' You can serialize the Dictionary now or do it later
    Dim dirInfoJson = JsonConvert.SerializeObject(DirectoryObjects)
    File.WriteAllText(jsonFile, dirInfoJson)
End Sub

You'll see that this JSON structure is identical to the one you're showing here.

Note:
There's the chance that File names in new File Systems are case sensitive, but this is not a problem because our string comparison is also case sensitive.

Sign up to request clarification or add additional context in comments.

1 Comment

This is a perfect solution, Jimi, thank you so much for your time and effort with this. For anyone else who finds this: the generic answer to my question is: create a class that mimics the JSON properties, and then make a nested Dictionary with as much nesting as your JSON and the last entry in the Dictionary as your class type. Then, make changes to the Dict object and serialize it back into the JSON file. Worth noting is that if your .json file is completely empty the code will fail. Your "blank" file should be "{}" so the code makes an empty object rather than no object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.