|
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.LibraryModel;
using NuGet.RuntimeModel;
using NuGet.Shared;
using NuGet.Versioning;
namespace NuGet.ProjectModel
{
/// <summary>
/// Writes out a PackageSpec object graph.
///
/// This is non-private only to facilitate unit testing.
/// </summary>
public sealed class PackageSpecWriter
{
/// <summary>
/// Writes a PackageSpec to an <c>NuGet.Common.IObjectWriter</c> instance.
/// </summary>
/// <param name="packageSpec">A <c>PackageSpec</c> instance.</param>
/// <param name="writer">An <c>NuGet.Common.IObjectWriter</c> instance.</param>
public static void Write(PackageSpec packageSpec, IObjectWriter writer)
{
Write(packageSpec, writer, hashing: false, EnvironmentVariableWrapper.Instance);
}
internal static void Write(PackageSpec packageSpec, IObjectWriter writer, bool hashing, IEnvironmentVariableReader environmentVariableReader)
{
Write(packageSpec, writer, hashing, environmentVariableReader, useLegacyWriter: false);
}
internal static void Write(PackageSpec packageSpec, IObjectWriter writer, bool hashing, IEnvironmentVariableReader environmentVariableReader, bool useLegacyWriter)
{
if (packageSpec == null)
{
throw new ArgumentNullException(nameof(packageSpec));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (!packageSpec.IsDefaultVersion)
{
SetValue(writer, "version", packageSpec.Version?.ToFullString());
}
SetMSBuildMetadata(writer, packageSpec, environmentVariableReader, useLegacyWriter);
SetFrameworks(writer, packageSpec.TargetFrameworks, hashing, useLegacyWriter);
JsonRuntimeFormat.WriteRuntimeGraph(writer, packageSpec.RuntimeGraph);
}
private static bool IsMetadataValid(ProjectRestoreMetadata msbuildMetadata)
{
if (msbuildMetadata == null)
{
return false;
}
if (msbuildMetadata.ProjectUniqueName == null && msbuildMetadata.ProjectName == null
&& msbuildMetadata.ProjectPath == null && msbuildMetadata.ProjectJsonPath == null
&& msbuildMetadata.PackagesPath == null && msbuildMetadata.OutputPath == null)
{
return false;
}
return true;
}
/// <summary>
/// This method sets the msbuild metadata that's important for restore. Ensures that frameworks regardless of which way they're stores in the metadata(full name or short tfm name) are written out the same.
/// </summary>
private static void SetMSBuildMetadata(IObjectWriter writer, PackageSpec packageSpec, IEnvironmentVariableReader environmentVariableReader, bool useTargetFrameworkAsKey)
{
var msbuildMetadata = packageSpec.RestoreMetadata;
if (!IsMetadataValid(msbuildMetadata))
{
return;
}
bool useMacros = MSBuildStringUtility.IsTrue(environmentVariableReader.GetEnvironmentVariable(MacroStringsUtility.NUGET_ENABLE_EXPERIMENTAL_MACROS));
var userSettingsDirectory = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory);
writer.WriteObjectStart(JsonPackageSpecReader.RestoreOptions);
SetValue(writer, "projectUniqueName", msbuildMetadata.ProjectUniqueName);
SetValue(writer, "projectName", msbuildMetadata.ProjectName);
SetValue(writer, "projectPath", msbuildMetadata.ProjectPath);
SetValue(writer, "projectJsonPath", msbuildMetadata.ProjectJsonPath);
SetValue(writer, "packagesPath", ApplyMacro(msbuildMetadata.PackagesPath, userSettingsDirectory, useMacros));
SetValue(writer, "outputPath", msbuildMetadata.OutputPath);
if (msbuildMetadata.ProjectStyle != ProjectStyle.Unknown)
{
SetValue(writer, "projectStyle", msbuildMetadata.ProjectStyle.ToString());
}
WriteMetadataBooleans(writer, msbuildMetadata);
if (useMacros)
{
var fallbackFolderCopy = msbuildMetadata.FallbackFolders.ToList();
var configFilePathsCopy = msbuildMetadata.ConfigFilePaths.ToList();
MacroStringsUtility.ApplyMacros(fallbackFolderCopy, userSettingsDirectory, MacroStringsUtility.UserMacro, PathUtility.GetStringComparisonBasedOnOS());
MacroStringsUtility.ApplyMacros(configFilePathsCopy, userSettingsDirectory, MacroStringsUtility.UserMacro, PathUtility.GetStringComparisonBasedOnOS());
SetArrayValue(writer, "fallbackFolders", fallbackFolderCopy);
SetArrayValue(writer, "configFilePaths", configFilePathsCopy);
}
else
{
SetArrayValue(writer, "fallbackFolders", msbuildMetadata.FallbackFolders);
SetArrayValue(writer, "configFilePaths", msbuildMetadata.ConfigFilePaths);
}
// This need to stay the original strings because the nuget.g.targets have conditional imports based on the original framework name
SetArrayValue(writer, "originalTargetFrameworks", msbuildMetadata.OriginalTargetFrameworks.OrderBy(c => c, StringComparer.Ordinal));
WriteMetadataSources(writer, msbuildMetadata);
WriteMetadataFiles(writer, msbuildMetadata);
WriteMetadataTargetFrameworks(writer, msbuildMetadata, useTargetFrameworkAsKey);
SetWarningProperties(writer, msbuildMetadata);
WriteNuGetLockFileProperties(writer, msbuildMetadata);
WriteNuGetAuditProperties(writer, msbuildMetadata.RestoreAuditProperties);
if (msbuildMetadata is PackagesConfigProjectRestoreMetadata pcMsbuildMetadata)
{
SetValue(writer, "packagesConfigPath", pcMsbuildMetadata.PackagesConfigPath);
}
if (packageSpec.RestoreMetadata.SdkAnalysisLevel is not null)
{
SetValue(writer, "SdkAnalysisLevel", packageSpec.RestoreMetadata.SdkAnalysisLevel.ToString());
}
writer.WriteObjectEnd();
}
private static string ApplyMacro(string value, string userSettingsDirectory, bool useMacros)
{
if (useMacros)
{
return MacroStringsUtility.ApplyMacro(value, userSettingsDirectory, MacroStringsUtility.UserMacro, PathUtility.GetStringComparisonBasedOnOS());
}
return value;
}
private static void WriteMetadataBooleans(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata)
{
SetValueIfTrue(writer, "crossTargeting", msbuildMetadata.CrossTargeting);
SetValueIfTrue(writer, "legacyPackagesDirectory", msbuildMetadata.LegacyPackagesDirectory);
SetValueIfTrue(writer, "validateRuntimeAssets", msbuildMetadata.ValidateRuntimeAssets);
SetValueIfTrue(writer, "skipContentFileWrite", msbuildMetadata.SkipContentFileWrite);
SetValueIfTrue(writer, "centralPackageVersionsManagementEnabled", msbuildMetadata.CentralPackageVersionsEnabled);
SetValueIfTrue(writer, "centralPackageFloatingVersionsEnabled", msbuildMetadata.CentralPackageFloatingVersionsEnabled);
SetValueIfTrue(writer, "centralPackageVersionOverrideDisabled", msbuildMetadata.CentralPackageVersionOverrideDisabled);
SetValueIfTrue(writer, "CentralPackageTransitivePinningEnabled", msbuildMetadata.CentralPackageTransitivePinningEnabled);
SetValueIfFalse(writer, "UsingMicrosoftNETSdk", msbuildMetadata.UsingMicrosoftNETSdk);
SetValueIfTrue(writer, "restoreUseLegacyDependencyResolver", msbuildMetadata.UseLegacyDependencyResolver);
SetValueIfTrue(writer, "restoreDoNotWriteDependencyGraphSpec", msbuildMetadata.RestoreDoNotWriteDependencyGraphSpec);
}
private static void WriteNuGetLockFileProperties(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata)
{
if (msbuildMetadata.RestoreLockProperties != null &&
(!string.IsNullOrEmpty(msbuildMetadata.RestoreLockProperties.RestorePackagesWithLockFile) ||
!string.IsNullOrEmpty(msbuildMetadata.RestoreLockProperties.NuGetLockFilePath) ||
msbuildMetadata.RestoreLockProperties.RestoreLockedMode))
{
writer.WriteObjectStart("restoreLockProperties");
SetValue(writer, "restorePackagesWithLockFile", msbuildMetadata.RestoreLockProperties.RestorePackagesWithLockFile);
SetValue(writer, "nuGetLockFilePath", msbuildMetadata.RestoreLockProperties.NuGetLockFilePath);
SetValueIfTrue(writer, "restoreLockedMode", msbuildMetadata.RestoreLockProperties.RestoreLockedMode);
writer.WriteObjectEnd();
}
}
private static void WriteNuGetAuditProperties(IObjectWriter writer, RestoreAuditProperties auditProperties)
{
if (auditProperties == null) return;
writer.WriteObjectStart("restoreAuditProperties");
SetValueIfNotNull(writer, "enableAudit", auditProperties.EnableAudit);
SetValueIfNotNull(writer, "auditLevel", auditProperties.AuditLevel);
SetValueIfNotNull(writer, "auditMode", auditProperties.AuditMode);
if (auditProperties.SuppressedAdvisories?.Count > 0)
{
writer.WriteObjectStart("suppressedAdvisories");
foreach (string advisory in auditProperties.SuppressedAdvisories)
{
writer.WriteNameValue(advisory, null);
}
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
}
private static void WriteMetadataTargetFrameworks(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata, bool useTargetFrameworkAsKey)
{
if (msbuildMetadata.TargetFrameworks?.Count > 0)
{
writer.WriteObjectStart("frameworks");
var frameworkNames = new HashSet<string>();
var frameworkSorter = NuGetFrameworkSorter.Instance;
foreach (var framework in msbuildMetadata.TargetFrameworks.OrderBy(c => c.TargetAlias, StringComparer.OrdinalIgnoreCase))
{
string frameworkHeader = useTargetFrameworkAsKey || string.IsNullOrEmpty(framework.TargetAlias)
? framework.FrameworkName.GetShortFolderName()
: framework.TargetAlias;
if (!frameworkNames.Contains(frameworkHeader))
{
frameworkNames.Add(frameworkHeader);
writer.WriteObjectStart(frameworkHeader);
SetValue(writer, "framework", framework.FrameworkName.GetShortFolderName());
SetValueIfNotNull(writer, "targetAlias", framework.TargetAlias);
writer.WriteObjectStart("projectReferences");
foreach (var project in framework.ProjectReferences.OrderBy(e => e.ProjectPath, PathUtility.GetStringComparerBasedOnOS()))
{
writer.WriteObjectStart(project.ProjectUniqueName);
writer.WriteNameValue("projectPath", project.ProjectPath);
if (project.IncludeAssets != LibraryIncludeFlags.All)
{
writer.WriteNameValue("includeAssets", LibraryIncludeFlagUtils.GetFlagString(project.IncludeAssets));
}
if (project.ExcludeAssets != LibraryIncludeFlags.None)
{
writer.WriteNameValue("excludeAssets", LibraryIncludeFlagUtils.GetFlagString(project.ExcludeAssets));
}
if (project.PrivateAssets != LibraryIncludeFlagUtils.DefaultSuppressParent)
{
writer.WriteNameValue("privateAssets", LibraryIncludeFlagUtils.GetFlagString(project.PrivateAssets));
}
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
writer.WriteObjectEnd();
}
}
writer.WriteObjectEnd();
}
}
private static void WriteMetadataFiles(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata)
{
if (msbuildMetadata.Files?.Count > 0)
{
writer.WriteObjectStart("files");
foreach (var file in msbuildMetadata.Files)
{
SetValue(writer, file.PackagePath, file.AbsolutePath);
}
writer.WriteObjectEnd();
}
}
private static void WriteMetadataSources(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata)
{
if (msbuildMetadata.Sources?.Count > 0)
{
writer.WriteObjectStart("sources");
foreach (var source in msbuildMetadata.Sources.OrderBy(e => e.Source, StringComparer.Ordinal))
{
writer.WriteObjectStart(source.Source);
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
}
}
private static void SetWarningProperties(IObjectWriter writer, ProjectRestoreMetadata msbuildMetadata)
{
if (msbuildMetadata.ProjectWideWarningProperties != null &&
(msbuildMetadata.ProjectWideWarningProperties.AllWarningsAsErrors ||
msbuildMetadata.ProjectWideWarningProperties.NoWarn.Count > 0 ||
msbuildMetadata.ProjectWideWarningProperties.WarningsAsErrors.Count > 0))
{
writer.WriteObjectStart("warningProperties");
SetValueIfTrue(writer, "allWarningsAsErrors", msbuildMetadata.ProjectWideWarningProperties.AllWarningsAsErrors);
if (msbuildMetadata.ProjectWideWarningProperties.NoWarn.Count > 0)
{
SetArrayValue(writer, "noWarn", msbuildMetadata
.ProjectWideWarningProperties
.NoWarn
.OrderBy(c => c)
.Select(c => c.GetName())
.Where(c => !string.IsNullOrEmpty(c)));
}
if (msbuildMetadata.ProjectWideWarningProperties.WarningsAsErrors.Count > 0)
{
SetArrayValue(writer, "warnAsError", msbuildMetadata
.ProjectWideWarningProperties
.WarningsAsErrors
.OrderBy(c => c)
.Select(c => c.GetName())
.Where(c => !string.IsNullOrEmpty(c)));
}
if (msbuildMetadata.ProjectWideWarningProperties.WarningsNotAsErrors.Count > 0)
{
SetArrayValue(writer, "warnNotAsError", msbuildMetadata
.ProjectWideWarningProperties
.WarningsNotAsErrors
.OrderBy(c => c)
.Select(c => c.GetName())
.Where(c => !string.IsNullOrEmpty(c)));
}
writer.WriteObjectEnd();
}
}
private static void SetDependencies(IObjectWriter writer, IEnumerable<LibraryDependency> libraryDependencies)
{
SetDependencies(writer, "dependencies", libraryDependencies.Where(dependency => dependency.LibraryRange.TypeConstraint != LibraryDependencyTarget.Reference));
SetDependencies(writer, "frameworkAssemblies", libraryDependencies.Where(dependency => dependency.LibraryRange.TypeConstraint == LibraryDependencyTarget.Reference));
}
/// <summary>
/// This method sorts the libraries based on the name
/// This method also writes out the normalized versions to avoid cases where original string is set because it was gotten through project system vs being installed from PM UI
/// </summary>
internal static void SetDependencies(IObjectWriter writer, string name, IEnumerable<LibraryDependency> libraryDependencies)
{
if (!libraryDependencies.Any())
{
return;
}
writer.WriteObjectStart(name);
foreach (var dependency in libraryDependencies.OrderBy(e => e.Name, StringComparer.Ordinal))
{
var expandedMode = dependency.IncludeType != LibraryIncludeFlags.All
|| dependency.SuppressParent != LibraryIncludeFlagUtils.DefaultSuppressParent
|| dependency.AutoReferenced
|| (dependency.LibraryRange.TypeConstraint != LibraryDependencyTarget.Reference
&& dependency.LibraryRange.TypeConstraint != (LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference))
|| !string.IsNullOrEmpty(dependency.Aliases)
|| dependency.GeneratePathProperty
|| dependency.VersionCentrallyManaged;
var versionRange = dependency.LibraryRange.VersionRange ?? VersionRange.All;
var versionString = versionRange.ToNormalizedString();
if (expandedMode)
{
writer.WriteObjectStart(dependency.Name);
if (dependency.IncludeType != LibraryIncludeFlags.All)
{
SetValue(writer, "include", dependency.IncludeType.AsString());
}
if (dependency.SuppressParent != LibraryIncludeFlagUtils.DefaultSuppressParent)
{
SetValue(writer, "suppressParent", dependency.SuppressParent.AsString());
}
if (dependency.LibraryRange.TypeConstraint != LibraryDependencyTarget.Reference
&& dependency.LibraryRange.TypeConstraint != (LibraryDependencyTarget.All & ~LibraryDependencyTarget.Reference))
{
SetValue(writer, "target", dependency.LibraryRange.TypeConstraint.AsString());
}
if (VersionRange.All.Equals(versionRange)
&& !dependency.LibraryRange.TypeConstraintAllows(LibraryDependencyTarget.Package)
&& !dependency.LibraryRange.TypeConstraintAllows(LibraryDependencyTarget.Reference)
&& !dependency.LibraryRange.TypeConstraintAllows(LibraryDependencyTarget.ExternalProject))
{
// Allow this specific case to skip the version property
}
else
{
SetValue(writer, "version", versionString);
}
if (dependency.VersionOverride != null)
{
SetValue(writer, "versionOverride", dependency.VersionOverride.ToNormalizedString());
}
SetValueIfTrue(writer, "autoReferenced", dependency.AutoReferenced);
if (dependency.NoWarn.Length > 0)
{
SetArrayValue(writer, "noWarn", dependency
.NoWarn
.Distinct()
.OrderBy(c => c)
.Select(code => code.GetName())
.Where(s => !string.IsNullOrEmpty(s)));
}
SetValueIfTrue(writer, "generatePathProperty", dependency.GeneratePathProperty);
SetValueIfTrue(writer, "versionCentrallyManaged", dependency.VersionCentrallyManaged);
SetValueIfNotNull(writer, "aliases", dependency.Aliases);
writer.WriteObjectEnd();
}
else
{
writer.WriteNameValue(dependency.Name, versionString);
}
}
writer.WriteObjectEnd();
}
/// <summary>
/// The central transitive dependecy groups are used for pack operation.
/// The metadata needed for pack is composed from:
/// Name, IncludeType, SuppressParent and Version
/// </summary>
internal static void SetCentralTransitveDependencyGroup(IObjectWriter writer, string name, IEnumerable<LibraryDependency> libraryDependencies)
{
if (!libraryDependencies.Any())
{
return;
}
writer.WriteObjectStart(name);
foreach (var dependency in libraryDependencies.OrderBy(e => e.Name, StringComparer.Ordinal))
{
var versionRange = dependency.LibraryRange.VersionRange ?? VersionRange.All;
var versionString = versionRange.ToNormalizedString();
writer.WriteObjectStart(dependency.Name);
if (dependency.IncludeType != LibraryIncludeFlags.All)
{
SetValue(writer, "include", dependency.IncludeType.ToString());
}
if (dependency.SuppressParent != LibraryIncludeFlagUtils.DefaultSuppressParent)
{
SetValue(writer, "suppressParent", dependency.SuppressParent.ToString());
}
SetValue(writer, "version", versionString);
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
}
private static void SetImports(IObjectWriter writer, ImmutableArray<NuGetFramework> frameworks)
{
if (frameworks.Length > 0)
{
var imports = frameworks.Select(framework => framework.GetShortFolderName());
writer.WriteNameArray("imports", imports);
}
}
private static void SetDownloadDependencies(IObjectWriter writer, ImmutableArray<DownloadDependency> downloadDependencies)
{
if (downloadDependencies.Length == 0)
{
return;
}
writer.WriteArrayStart("downloadDependencies");
foreach (var dependency in downloadDependencies.GroupBy(dep => dep.Name).OrderBy(dep => dep.Key))
{
var version = string.Join(";", dependency.Select(dep => dep.VersionRange).OrderBy(dep => dep.MinVersion).Select(dep => dep.ToNormalizedString()));
writer.WriteObjectStart();
SetValue(writer, "name", dependency.Key);
SetValue(writer, "version", version);
writer.WriteObjectEnd();
}
writer.WriteArrayEnd();
}
private static void SetFrameworks(IObjectWriter writer, IList<TargetFrameworkInformation> frameworks, bool hashing, bool useTargetFrameworkAsKey)
{
if (frameworks.Count > 0)
{
writer.WriteObjectStart("frameworks");
var frameworkSorter = NuGetFrameworkSorter.Instance;
foreach (var framework in frameworks.OrderBy(c => c.TargetAlias, StringComparer.OrdinalIgnoreCase))
{
string frameworkHeader = useTargetFrameworkAsKey || string.IsNullOrEmpty(framework.TargetAlias)
? framework.FrameworkName.GetShortFolderName()
: framework.TargetAlias;
writer.WriteObjectStart(frameworkHeader);
SetValue(writer, "framework", framework.FrameworkName.GetShortFolderName());
SetValueIfNotNull(writer, "targetAlias", framework.TargetAlias);
SetDependencies(writer, framework.Dependencies);
SetCentralDependencies(writer, framework.CentralPackageVersions.Count, framework.CentralPackageVersions.Values, hashing);
SetImports(writer, framework.Imports);
SetValueIfTrue(writer, "assetTargetFallback", framework.AssetTargetFallback);
SetValueIfNotNull(writer, "secondaryFramework",
(DeconstructFallbackFrameworks(framework.FrameworkName) as DualCompatibilityFramework)?.SecondaryFramework.GetShortFolderName());
SetValueIfTrue(writer, "warn", framework.Warn);
SetDownloadDependencies(writer, framework.DownloadDependencies);
SetFrameworkReferences(writer, framework.FrameworkReferences);
SetValueIfNotNull(writer, "runtimeIdentifierGraphPath", framework.RuntimeIdentifierGraphPath);
SetPackagesToPrune(writer, framework.PackagesToPrune, hashing);
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
}
}
// An AssetTargetFallbackFramework or FallbackFramework *might* have a DualCompatibilityFramework as it's root.
private static NuGetFramework DeconstructFallbackFrameworks(NuGetFramework nuGetFramework)
{
if (nuGetFramework is AssetTargetFallbackFramework assetTargetFallbackFramework)
{
return assetTargetFallbackFramework.RootFramework;
}
if (nuGetFramework is FallbackFramework fallbackFramework)
{
return fallbackFramework;
}
return nuGetFramework;
}
private static void SetFrameworkReferences(IObjectWriter writer, IReadOnlyCollection<FrameworkDependency> frameworkReferences)
{
if (frameworkReferences?.Count > 0)
{
writer.WriteObjectStart("frameworkReferences");
foreach (var dependency in frameworkReferences.OrderBy(dep => dep))
{
writer.WriteObjectStart(dependency.Name);
SetValue(writer, "privateAssets", FrameworkDependencyFlagsUtils.GetFlagString(dependency.PrivateAssets));
writer.WriteObjectEnd();
}
writer.WriteObjectEnd();
}
}
private static void SetCentralDependencies(IObjectWriter writer, int count, IEnumerable<CentralPackageVersion> centralPackageVersions, bool hashing)
{
if (count == 0)
{
return;
}
writer.WriteObjectStart("centralPackageVersions");
foreach (var dependency in centralPackageVersions.OrderBy(dep => dep.Name, StringComparer.OrdinalIgnoreCase))
{
writer.WriteNameValue(name: dependency.Name, value: dependency.VersionRange.OriginalString ?? dependency.VersionRange.ToNormalizedString());
}
writer.WriteObjectEnd();
}
private static void SetPackagesToPrune(IObjectWriter writer, IReadOnlyDictionary<string, PrunePackageReference> packagesToPrune, bool hashing)
{
if (packagesToPrune.Count == 0)
{
return;
}
writer.WriteObjectStart("packagesToPrune");
foreach (var dependency in packagesToPrune.OrderBy(dep => dep.Key, StringComparer.OrdinalIgnoreCase))
{
writer.WriteNameValue(name: dependency.Key, value: dependency.Value.VersionRange.OriginalString ?? dependency.Value.VersionRange.ToNormalizedString());
}
writer.WriteObjectEnd();
}
private static void SetValueIfTrue(IObjectWriter writer, string name, bool value)
{
if (value)
{
writer.WriteNameValue(name, value);
}
}
private static void SetValueIfFalse(IObjectWriter writer, string name, bool value)
{
if (!value)
{
writer.WriteNameValue(name, value);
}
}
private static void SetValueIfNotNull(IObjectWriter writer, string name, string value)
{
if (!string.IsNullOrEmpty(value))
{
writer.WriteNameValue(name, value);
}
}
private static void SetValue(IObjectWriter writer, string name, string value)
{
if (value != null)
{
writer.WriteNameValue(name, value);
}
}
private static void SetArrayValue(IObjectWriter writer, string name, IEnumerable<string> values)
{
if (values != null)
{
writer.WriteNonEmptyNameArray(name, values);
}
}
}
}
|