Microsoft.WebTools.Aspire.targets 的“<”解析错误
结论
Microsoft.WebTools.Aspire.targets 通常不存在 XML 语法错误,Azure.Identity 也没有在运行时调用 Aspire。报错发生在 Visual Studio Web Tools 的设计时构建阶段:GenerateCombinedComponentSchema 或 GenerateCombinedAppSettingsSchema 任务合并 JSON Schema 时,读到了以 < 开头的 XML 内容,于是 System.Text.Json 报错:
'<' is an invalid start of a value.
错误位置指向 .targets 中的 <GenerateCombinedAppSettingsSchema>,说明异常是在此处调用 MSBuild task 时抛出的。这不代表该段 XML 解析失败,也不需要直接修改这个文件。
如果问题确实从 Azure.Identity 1.20.0 开始出现,可能的触发过程是:NuGet restore 改变了设计时构建的输入,或者改变了传入的 @(JsonSchemaSegment),Visual Studio 安装的 Web Tools 任务随后在处理这些输入时失败。现有错误不足以判断问题来自 Azure.Identity 包、Visual Studio Web Tools 的兼容性,还是本机 NuGet 缓存损坏。要确定原因,必须查看实际传给 MSBuild 的输入。
IEnumerable 失去语义着色和悬停说明,很可能也是设计时构建失败引起的。Roslyn 无法完整取得引用、编译选项或生成文件,编辑器的语义服务便无法正常工作。项目仍可运行,是因为普通构建、已有输出和设计时构建执行的目标并不完全相同。
为什么没有引用 Aspire 也会执行该文件
这里的 Aspire 指 Visual Studio Web Tools 安装目录中的设计时工具,与项目是否直接引用 .NET Aspire NuGet package 无关。
加载 Web 项目时,Visual Studio 可以从自身安装目录导入类似下面的文件:
MSBuild\Microsoft\VisualStudio\WebTools.Aspire\Microsoft.WebTools.Aspire.targets
文件中的目标带有:
BeforeTargets="CompileDesignTime"
因此,它主要参与 IDE 的设计时编译。只要导入链产生了 @(JsonSchemaSegment),以下条件就会成立:
Condition=" '@(JsonSchemaSegment)' != '' "
随后,Web Tools 会尝试生成:
CombinedComponentSchema.json
AppSettingsSchema.json
排查时应确认以下几个值在失败时具体指向哪些文件:
@(JsonSchemaSegment)
$(AppSettingJsonSchemaLoadFilePath)
$(JsonSchemaCombinedFilePath)
$(AppSettingsJsonSchemaCombinedFilePath)
XML 验证为什么不能说明问题
确认 Microsoft.WebTools.Aspire.targets 是有效 XML,只能说明 MSBuild 可以读取该文件。当前错误来自 JSON 解析器,所以需要检查 task 当作 JSON 读取的输入文件。
LineNumber: 2 | BytePositionInLine: 0 是 JSON 解析器报告的位置。输入文件的这个位置出现 <,通常有以下几种原因:
- 输入实际是 XML 文件;
- 文件扩展名是
.json,内容却被错误写成了 XML; - NuGet 源、代理或认证服务返回了 HTML/XML 错误页;
obj中的合并 Schema 是上次失败后留下的损坏文件;@(JsonSchemaSegment)意外包含了.targets、.props或其他非 JSON 文件。
单击错误后跳转到 .targets 文件,只能说明这里是 task 的调用位置,无法证明 JSON 解析器读取的就是这个 .targets 文件。
先确认 Azure.Identity 的版本关联
在同一台机器、同一个 commit 和相同 SDK 下进行一次最小对照。先使用:
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.19.x" />
</ItemGroup>
清理、还原并构建后,再改回:
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.20.0" />
</ItemGroup>
这里的 1.19.x 代表已经确认正常的 1.20.0 之前版本。测试时应替换为项目实际验证过的完整版本号,不要在正式项目中直接使用通配版本。
如果项目使用中央包管理,请修改 Directory.Packages.props:
<ItemGroup>
<PackageVersion Include="Azure.Identity" Version="已验证的完整版本号" />
</ItemGroup>
每次切换版本后,都要清除项目生成目录和 NuGet 缓存,以免旧文件干扰对照结果。
生成 MSBuild 诊断日志
关闭 Visual Studio,然后在项目目录中执行:
dotnet build .\Project.csproj -bl:azure-identity.binlog -v:diag
再生成展开后的项目:
dotnet msbuild .\Project.csproj /pp:expanded-project.xml
在诊断日志和 expanded-project.xml 中搜索:
JsonSchemaSegment
AppSettingJsonSchemaLoadFilePath
GenerateCombinedComponentSchema
GenerateCombinedAppSettingsSchema
Microsoft.WebTools.Aspire.targets
Azure.Identity
可以使用 MSBuild Structured Log Viewer 打开二进制日志。查看 GenerateCombinedComponentSchemaTarget 和 GenerateCombinedAppSettingsSchemaTarget 执行前收到的参数,不要只看最后一条异常。
需要逐项确认:
@(JsonSchemaSegment)包含哪些完整路径;- 这些路径是否存在;
- 对应文件是否确实为 JSON;
AppSettingJsonSchemaLoadFilePath是否错误指向 XML、HTML 或.targets文件;CombinedComponentSchema.json和AppSettingsSchema.json的内容是否完整;- 相关 item 是由哪个
.props、.targets或 NuGet package 加入的。
还可以列出直接依赖和传递依赖:
dotnet list .\Project.csproj package --include-transitive
较新的 SDK 可能会提示改用 dotnet package list,使用当前 SDK 支持的形式即可。
检查实际输入文件
从日志中找到完整路径后,直接查看文件开头。PowerShell 示例:
Get-Content "日志中显示的文件完整路径" -TotalCount 10
有效的 JSON Schema 通常以 { 开始,例如:
{
"$schema": "https://json-schema.org/draft/2020-12/schema"
}
如果文件以以下任一内容开头,就找到了错误输入:
<?xml version="1.0"?>
<!DOCTYPE html>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
如果扩展名是 .json,内容却是登录页、403/404 页面或代理返回的信息,还应检查企业代理、私有 NuGet 源和凭据状态。如果输入路径直接指向 .targets,请在展开后的项目中继续追查是谁将该路径加入了 @(JsonSchemaSegment)。
清理本机状态
如果只有一名开发人员遇到问题,应着重检查本机缓存和 Visual Studio 组件版本。关闭所有 Visual Studio 实例后执行:
dotnet nuget locals all --clear
dotnet clean .\Project.csproj
dotnet restore .\Project.csproj --force --no-cache
dotnet build .\Project.csproj -bl:after-clean.binlog
同时删除受影响项目的以下生成目录:
bin
obj
.vs
这些目录通常不应包含需要保留的源文件。删除前仍要确认项目没有误将手工文件放在其中。
完成后重新打开 solution。NuGet assets、合并 Schema 和 Visual Studio 的 solution 缓存会重新生成。系统重装后问题曾暂时消失,符合缓存或组件状态恢复正常、依赖重新还原后问题再次出现的现象,但这一现象还不能证明根因。
可用的解决办法
1. 暂时锁定已验证可用的 Azure.Identity 版本
如果对照测试确认问题只会由 1.20.0 或更高版本触发,可以暂时锁定到团队已验证的较低版本,并保留两个版本的 binlog。
降级也会失去新版本中的修复和功能。使用前应确认项目是否依赖新 API,以及旧版本能否满足安全和兼容性要求。
2. 统一 Visual Studio、SDK 和 NuGet 环境
比较正常机器与故障机器上的以下输出:
dotnet --info
dotnet --list-sdks
dotnet nuget list source
还要比较以下环境信息:
- Visual Studio 的完整版本和更新通道;
- ASP.NET and web development workload;
- 已安装的 .NET SDK;
NuGet.Config;global.json;- 环境变量和企业代理配置;
- 实际解析出的传递依赖版本。
即使两台机器都使用最新版,环境也未必一致。预览通道、更新批次、SDK feature band 或 workload 组件不同,都可能导致设计时构建使用不同版本的 task。
如果怀疑 Visual Studio 文件损坏,应先通过 Visual Studio Installer 执行 Repair,不要手工编辑或替换:
Microsoft.WebTools.Aspire.targets
Microsoft.WebTools.Aspire.MSBuild.dll
这些文件属于 Visual Studio 的安装内容。手工修改可能影响所有 Web 项目,之后也会被更新或修复操作覆盖。
3. 仅在日志确认后排除错误的 Schema item
如果 binlog 已确认某个非 JSON 文件被错误加入 @(JsonSchemaSegment),可以在项目或 Directory.Build.targets 中有针对性地临时排除。例如:
<Project>
<Target Name="RemoveInvalidJsonSchemaSegment"
BeforeTargets="GenerateCombinedComponentSchemaTarget">
<ItemGroup>
<JsonSchemaSegment Remove="C:\完整路径\错误输入文件.targets" />
</ItemGroup>
</Target>
</Project>
路径必须取自实际日志,不能照抄示例。也不要无条件删除全部 @(JsonSchemaSegment),否则合法的 appsettings Schema 功能可能失效。请为这个 workaround 添加注释,并在 Visual Studio 或相关 package 修复后将其移除。
如果错误输入来自某个 NuGet package 的 build 或 buildTransitive 资产,更合适的临时措施是锁定 package 版本并提交可复现日志,避免长期覆盖 Visual Studio 的目标。
恢复 IEnumerable 的着色和悬停提示
解决设计时构建错误后,清理 obj 和 .vs,再重新加载 solution。可以按以下顺序操作:
- 关闭 Visual Studio;
- 删除受影响项目的
bin、obj和 solution 的.vs; - 清除 NuGet 缓存并重新 restore;
- 确认命令行构建不再出现 Schema 错误;
- 重新打开 Visual Studio,等待依赖还原和项目分析完成;
- 如有必要,卸载并重新加载受影响项目,或者重启 Visual Studio。
如果构建错误已经消失,IEnumerable 仍没有语义着色和 Quick Info,请继续检查:
- 文件是否被当作普通文本打开;
- 项目是否处于 unloaded 或 incompatible 状态;
System.Collections.Generic所在目标框架的引用是否解析成功;- Error List 中是否还存在 design-time build 或 language service 错误;
- 关闭第三方扩展后,问题是否仍然存在。
这个问题不需要为 IEnumerable 添加特殊引用。项目模型、框架引用和 Roslyn 设计时构建恢复正常后,它的语义着色与悬停说明通常也会恢复。
提交问题时应附带什么
如果清理缓存后仍能稳定复现,建议准备以下材料:
- 可以复现问题的最小项目;
- 正常版本和异常版本各自生成的 binlog;
dotnet --info输出;- Visual Studio 完整版本;
dotnet list package --include-transitive输出;- 展开项目中
JsonSchemaSegment的来源; - 被错误读取文件的路径和前几行内容;
- 正常机器与故障机器之间的环境差异。
在获得这些信息之前,更准确的描述是:“升级 Azure.Identity 后,可以重复触发 Web Tools 的 JSON Schema 生成错误。”错误指向 Microsoft.WebTools.Aspire.targets,并不足以证明该 XML 文件已经损坏,也不能证明 Azure.Identity 直接将 MSBuild XML 当作 JSON 解析。
备注:内容仅供参考。
