You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PowerShell脚本中更新JSON键字段?解决字段未找到错误

问题分析与解决

错误根源

你的JSON结构中record是数组,每个元素是仅包含单个日期键(如20230214)的对象,但代码中直接通过$json.record.$date访问,这是错误的——数组没有名为日期值的属性,必须先遍历数组找到包含目标日期键的元素,这就是"字段未找到"错误的核心原因。

另外还有两个隐性问题:

  • JSON示例用total_active字段,但代码中用total_active_minutes,字段名不统一
  • 时间差计算时如果outTime早于inTime,会得到负数,不符合实际逻辑

修复后的完整代码

<#
Set-UserStatus.ps1
****************************************************************
* ATTACH THIS SCRIPT TO USER LOGIN & LOGOUT  *
****************************************************************
#>

<#
    Logon – 4624 (Security event log)  -- 4634
    Logoff – 4647 (Security event log)
    Startup – 6005 (System event log)
    RDP Session Reconnect – 4778 (Security event log)
    RDP Session Disconnect – 4779 (Security event log)
    Locked – 4800 (Security event log)
    Unlocked – 4801 (Security event log)
#>

function Add-to-JSON{
    param (
        [String] $date,
        [String] $name,
        [String] $compName,
        [String] $inTime,
        [String] $outTime
    )

    Write-Output $date $name $compName $inTime $outTime

    $jsonfile = "E:\RK\Projects\Extras\tmp\"+$compName+"_"+$name+"_7.json"
    
    # 初始化或读取JSON
    if (!(Test-Path -Path $jsonfile -PathType Leaf)){    
        $json = [PSCustomObject]@{record = @()}             
    }
    else{
        $json = Get-Content -Path $jsonfile | Out-String | ConvertFrom-Json
    }

    # 处理时间差,确保结束时间不早于开始时间
    $start = [DateTime]$inTime
    $end = [DateTime]$outTime
    if ($end -lt $start) {
        $temp = $start
        $start = $end
        $end = $temp
    }
    $time_diff = (New-TimeSpan -Start $start -End $end).TotalMinutes

    $alreadyAddedFlag = $false
    # 查找已有日期记录
    $existingRecord = $json.record | Where-Object { $_ | Get-Member -Name $date -MemberType NoteProperty }

    if (-not $existingRecord){       
        # 新增日期记录
        $new_session = @{ 
            "sin_time"= $inTime; 
            "sout_time"=$outTime; 
            "message"=""; 
            "active_time"= $time_diff 
        }
        $today_dt =  [PSCustomObject]@{
            "in_time"=$inTime; 
            "out_time"=$outTime; 
            "total_active"= $time_diff ; 
            "session"= @($new_session) 
        }
        # 包装成带日期键的对象
        $dateEntry = [PSCustomObject]@{$date = $today_dt}
        $json.record += $dateEntry     
    }
    else{
        # 获取日期对应的具体数据
        $dateData = $existingRecord.$date
        if ($dateData.out_time -eq $outTime){   
            $alreadyAddedFlag = $true       
        }
        else{           
            # 更新总时长和退出时间
            $dateData.total_active = $dateData.total_active + $time_diff
            $dateData.out_time = $outTime  
            # 新增会话
            $new_session = @{ 
                "sin_time"= $inTime; 
                "sout_time"=$outTime; 
                "message"=""; 
                "active_time"= $time_diff 
            }    
            $dateData.session += $new_session      
        }
    }

    Write-Output  $json.record $alreadyAddedFlag

    if (!$alreadyAddedFlag){
        $json | ConvertTo-Json -Depth 100 | Out-File $jsonfile -Encoding utf8
    }    
}

function Get-EntryExitTime{
    $username, $computername, $date =  Get-Details
    $startTime = ""
    $endTime = ""

    # 获取最晚的进入/解锁时间作为开始时间
    $time1 = Get-Time -ComputerName $computername -Username $username -process "Login"
    $time2 = Get-Time -ComputerName $computername -Username $username -process "Unlock"
    $startTime = if ($time1 -ge $time2) { $time1 } else { $time2 }
   
    # 获取最晚的退出/锁定时间作为结束时间
    $time1 = Get-Time -ComputerName $computername -Username $username -process "Logout"
    $time2 = Get-Time -ComputerName $computername -Username $username -process "Lock"
    $endTime = if ($time1 -ge $time2) { $time1 } else { $time2 }     

    Add-to-JSON -date $date -name $username -compName $computername -inTime $startTime -outTime $endTime
}

function Get-Time{
    param(  
        [Parameter (Mandatory = $true)] [String]$ComputerName,
        [Parameter (Mandatory = $true)] [String]$Username,
        [Parameter (Mandatory = $true)] [ValidateSet("Login","Unlock","Lock","Logout")]  [String]$process 
    )

    $cmds = @{Login=4624; Unlock=4801; Lock=4800; Logout=4647}
    $cmd = Get-EventLog -LogName Security -ComputerName $ComputerName -InstanceId $cmds[$process] -Message *$Username* -Newest 1
    return $cmd.TimeGenerated
}

function Get-Details{
    $username = $((Get-WMIObject -class Win32_ComputerSystem).username -split "\\")[1]
    $computername = $env:COMPUTERNAME
    $date = (Get-Date).ToString('yyyyMMdd')

    return $username, $computername, $date
}

Get-EntryExitTime

关键改动说明

  1. 日期记录查找逻辑:用Where-Object结合Get-Member遍历record数组,找到包含目标日期键的元素,替代原代码中直接访问数组属性的错误方式
  2. 字段名统一:将代码中的total_active_minutes、active_time_minutes改为与JSON示例一致的total_active、active_time
  3. 时间差修正:先判断开始/结束时间的先后顺序,避免出现负的时长
  4. 数组类型处理:新增会话时确保session始终是数组(原代码中第一次添加时是单个对象,后续添加会出错)
  5. 编码规范:输出JSON时指定-Encoding utf8,避免中文乱码问题

内容的提问来源于stack exchange,提问作者R.K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 01:01:51