【问题标题】:How to make my VB.NET code dynamic instead of static?如何使我的 VB.NET 代码动态而不是静态?
【发布时间】:2020-12-24 22:26:57
【问题描述】:

下面的代码给了我错误system.argumentexception an element with the same key already exists。当我在 Friend Sub Test 中使用以下行时:'Dim str_rootdirectory As String = Directory.GetCurrentDirectory() ' "C:\TEMP" 它可以工作。有什么区别?

我的 VB.NET 代码:

Public Class Form1
    Public Sub recur_getdirectories(ByVal di As DirectoryInfo)
        For Each directory As DirectoryInfo In di.GetDirectories()
            'get each directory and call the module main to get the security info and write to json
            Call Module1.Main(directory.FullName)
            recur_getdirectories(directory)
        Next
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim rootDirectory As String = TextBox1.Text.Trim()
        Dim di As New DirectoryInfo(rootDirectory)

        'get directories recursively and work with each of them
        recur_getdirectories(di)
    End Sub
End Class

Public Module RecursiveEnumerableExtensions
    Iterator Function Traverse(Of T)(ByVal root As T, ByVal children As Func(Of T, IEnumerable(Of T)), ByVal Optional includeSelf As Boolean = True) As IEnumerable(Of T)
        If includeSelf Then Yield root
        Dim stack = New Stack(Of IEnumerator(Of T))()

        Try
            stack.Push(children(root).GetEnumerator())
            While stack.Count <> 0
                Dim enumerator = stack.Peek()
                If Not enumerator.MoveNext() Then
                    stack.Pop()
                    enumerator.Dispose()
                Else
                    Yield enumerator.Current
                    stack.Push(children(enumerator.Current).GetEnumerator())
                End If
            End While
        Finally
            For Each enumerator In stack
                enumerator.Dispose()
            Next
        End Try
    End Function
End Module

Public Module TestClass
    Function GetFileSystemAccessRule(d As DirectoryInfo) As IEnumerable(Of FileSystemAccessRule)
        Dim ds As DirectorySecurity = d.GetAccessControl()
        Dim arrRules As AuthorizationRuleCollection = ds.GetAccessRules(True, True, GetType(Security.Principal.NTAccount))

        For Each authorizationRule As FileSystemAccessRule In arrRules
            Dim strAclIdentityReference As String = authorizationRule.IdentityReference.ToString()
            Dim strInheritanceFlags As String = authorizationRule.InheritanceFlags.ToString()
            Dim strAccessControlType As String = authorizationRule.AccessControlType.ToString()
            Dim strFileSystemRights As String = authorizationRule.FileSystemRights.ToString()
            Dim strIsInherited As String = authorizationRule.IsInherited.ToString()
        Next

        ' This function should return the following values, because they should be mentoined in the JSON:
        ' IdentityReference = strAclIdentityReference
        ' InheritanceFlags = strInheritanceFlags
        ' AccessControlType = strAccessControlType
        ' FileSystemRights = strFileSystemRights
        ' IsInherited = strIsInherited

        Return ds.GetAccessRules(True, True, GetType(System.Security.Principal.NTAccount)).Cast(Of FileSystemAccessRule)()
    End Function

    Friend Sub Test(ByVal curDirectory As String)
        'Dim str_rootdirectory As String = Directory.GetCurrentDirectory() ' "C:\TEMP"
        Dim str_rootdirectory As String = curDirectory

        Dim di As DirectoryInfo = New DirectoryInfo(str_rootdirectory)

        Dim directoryQuery = RecursiveEnumerableExtensions.Traverse(di, Function(d) d.GetDirectories())

        Dim list = directoryQuery.Select(
            Function(d) New With {
                .directory = d.FullName,
                .permissions = {
                    GetFileSystemAccessRule(d).ToDictionary(Function(a) a.IdentityReference.ToString(), Function(a) a.FileSystemRights.ToString())
                }
            }
        )
        Dim json = JsonConvert.SerializeObject(list, Formatting.Indented)
        File.WriteAllText("ABCD.json", json)
    End Sub
End Module

Public Module Module1
    Public Sub Main(ByVal curDirectory As String)
        Console.WriteLine("Environment version: " & Environment.Version.ToString())
        Console.WriteLine("Json.NET version: " & GetType(JsonSerializer).Assembly.FullName)
        Console.WriteLine("")
        Try
            TestClass.Test(curDirectory)

        Catch ex As Exception
            Console.WriteLine("Unhandled exception: ")
            Console.WriteLine(ex)
            Throw
        End Try
    End Sub
End Module

我的示例文件夹结构:

文件夹: "C:\Temp"

权限: SecurityGroup-A 拥有完全控制权, SecurityGroup-B 有修改权限

文件夹: "C:\Temp\Folder_A"

权限: SecurityGroup-C 拥有完全控制权

但这只是两个文件夹的示例。实际上,它将运行数百个带有子文件夹的文件夹。因此 JSON 将扩展。

我的json输出期望:

[{
        "directory": "C:\\TEMP",
        "permissions": [{
                "IdentityReference": "CONTOSO\\SecurityGroup-A",
                "AccessControlType": "Allow",
                "FileSystemRights": "FullControl",
                "IsInherited": "TRUE"
            }, {
                "IdentityReference": "CONTOSO\\SecurityGroup-B",
                "AccessControlType": "Allow",
                "FileSystemRights": "Modify",
                "IsInherited": "False"
            }
        ]
    }, {
        "directory": "C:\\TEMP\\Folder_A",
        "permissions": [{
                "IdentityReference": "CONTOSO\\SecurityGroup-C",
                "AccessControlType": "Allow",
                "FileSystemRights": "Full Control",
                "IsInherited": "False"
            }
        ]
    }
]

【问题讨论】:

  • 您的 JSON 包含重复的属性名称 -- "RandomValue01" 在同一个 permissions 对象中出现两次。这是您的问题中的错字还是您真的需要这个?
  • 最新的JSON RFC 声明当对象中的名称不唯一时,接收此类对象的软件的行为是不可预测的。 所以我不推荐允许在单个 JSON 对象中重复名称。
  • 感谢您提供的信息,但带有随机值的 json 输出只是一个示例,以便更好地理解我的代码。实际上,这些值将是唯一的。我的问题更多基于我的 VB.NET 代码,它会随着每个新的 RandomValue 一起增长——我想防止这种情况发生,但是如何防止呢?
  • 好的,在这种情况下,您可以将permissions 属性定义为List(Of Dictionary(Of String, String))。 Json.NET(和其他 JSON 序列化器,如 System.Text.Json)会将字典序列化为带有动态键的 JSON 对象。演示在这里:dotnetfiddle.net/OeonWQ。这能回答你的问题吗?
  • 谢谢,但我认为我无法正确提出我的请求。让我们根据您的代码继续我的问题。想象在你的代码中你有一个新的子。在 sub 里面有一个代码,它将递归地循环通过你的 windows 文件系统并得到a) all directorys in your windows explorerb) read the security permissions of each directory。您将收到的数据超过千倍。但是您希望以 JSON 格式输出。如果不使用line 31/38 for directoryline 33/34/40 for RandomValues 中的hardcoded values,您将如何在代码中实现这一点?

标签: json vb.net json.net


【解决方案1】:

您当前的 JSON 使用 [*].permissions[*] 对象的静态属性名称,因此无需尝试通过 ToDictionary() 将它们的列表转换为具有可变键名称的字典:

' This is not needed
.permissions = {
    GetFileSystemAccessRule(d).ToDictionary(Function(a) a.IdentityReference.ToString(), Function(a) a.FileSystemRights.ToString())
}

相反,将每个FileSystemAccessRule 转换为一些适当的DTO 以进行序列化。 anonymous type object 非常适合此目的:

Public Module DirectoryExtensions
    Function GetFileSystemAccessRules(d As DirectoryInfo) As IEnumerable(Of FileSystemAccessRule)
        Dim ds As DirectorySecurity = d.GetAccessControl()
        Dim arrRules As AuthorizationRuleCollection = ds.GetAccessRules(True, True, GetType(Security.Principal.NTAccount))
        Return arrRules.Cast(Of FileSystemAccessRule)()
    End Function
    
    Public Function SerializeFileAccessRules(ByVal curDirectory As String, Optional ByVal formatting As Formatting = Formatting.Indented)
        Dim di As DirectoryInfo = New DirectoryInfo(curDirectory)

        Dim directoryQuery = RecursiveEnumerableExtensions.Traverse(di, Function(d) d.GetDirectories())

        Dim list = directoryQuery.Select(
            Function(d) New With {
                .directory = d.FullName,
                .permissions = GetFileSystemAccessRules(d).Select(
                    Function(a) New With { 
                        .IdentityReference = a.IdentityReference.ToString(),
                        .AccessControlType = a.AccessControlType.ToString(),
                        .FileSystemRights = a.FileSystemRights.ToString(),
                        .IsInherited = a.IsInherited.ToString()
                    }
                )
            }
        )
        Return JsonConvert.SerializeObject(list, formatting)    
    End Function
End Module

Public Module RecursiveEnumerableExtensions
    ' Translated to vb.net from this answer https://stackoverflow.com/a/60997251/3744182
    ' To https://stackoverflow.com/questions/60994574/how-to-extract-all-values-for-all-jsonproperty-objects-with-a-specified-name-fro
    ' which was rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
    ' to "Efficient graph traversal with LINQ - eliminating recursion" https://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
    Iterator Function Traverse(Of T)(ByVal root As T, ByVal children As Func(Of T, IEnumerable(Of T)), ByVal Optional includeSelf As Boolean = True) As IEnumerable(Of T)
        If includeSelf Then Yield root
        Dim stack = New Stack(Of IEnumerator(Of T))()

        Try
            stack.Push(children(root).GetEnumerator())
            While stack.Count <> 0
                Dim enumerator = stack.Peek()
                If Not enumerator.MoveNext() Then
                    stack.Pop()
                    enumerator.Dispose()
                Else
                    Yield enumerator.Current
                    stack.Push(children(enumerator.Current).GetEnumerator())
                End If
            End While
        Finally
            For Each enumerator In stack
                enumerator.Dispose()
            Next
        End Try
    End Function
End Module

演示小提琴here(不幸的是,由于客户端代码的安全限制,它不适用于https://dotnetfiddle.net,但应该可以在完全信任的情况下运行)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-07
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 2013-05-25
    • 1970-01-01
    • 2012-10-04
    相关资源
    最近更新 更多