Skip to content

Commit

Permalink
feat: JSON数组访问支持索引和范围运算符
Browse files Browse the repository at this point in the history
  • Loading branch information
stratosblue committed Aug 11, 2024
1 parent 95e9523 commit ba8607a
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 32 deletions.
110 changes: 110 additions & 0 deletions CodeMaid.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="SteveCadwallader.CodeMaid.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</sectionGroup>
</configSections>
<userSettings>
<SteveCadwallader.CodeMaid.Properties.Settings>
<setting name="Reorganizing_MemberTypeFields" serializeAs="String">
<value>Fields||3||字段</value>
</setting>
<setting name="Reorganizing_MemberTypeProperties" serializeAs="String">
<value>Properties||4||属性</value>
</setting>
<setting name="Reorganizing_MemberTypeMethods" serializeAs="String">
<value>Methods||9||方法</value>
</setting>
<setting name="Reorganizing_MemberTypeDelegates" serializeAs="String">
<value>Delegates||1||委托</value>
</setting>
<setting name="Reorganizing_MemberTypeEvents" serializeAs="String">
<value>Events||2||事件</value>
</setting>
<setting name="Reorganizing_MemberTypeInterfaces" serializeAs="String">
<value>Interfaces||8||接口</value>
</setting>
<setting name="Reorganizing_MemberTypeConstructors" serializeAs="String">
<value>Constructors||6||构造函数</value>
</setting>
<setting name="Reorganizing_MemberTypeEnums" serializeAs="String">
<value>Enums||10||枚举</value>
</setting>
<setting name="Reorganizing_MemberTypeDestructors" serializeAs="String">
<value>Destructors||7||析构函数</value>
</setting>
<setting name="Reorganizing_MemberTypeIndexers" serializeAs="String">
<value>Indexers||5||索引器</value>
</setting>
<setting name="Reorganizing_MemberTypeClasses" serializeAs="String">
<value>Classes||12||类</value>
</setting>
<setting name="Reorganizing_MemberTypeStructs" serializeAs="String">
<value>Structs||11||结构体</value>
</setting>
<setting name="Reorganizing_RegionsInsertNewRegions" serializeAs="String">
<value>True</value>
</setting>
<setting name="Reorganizing_RunAtStartOfCleanup" serializeAs="String">
<value>True</value>
</setting>
<setting name="Reorganizing_RegionsRemoveExistingRegions" serializeAs="String">
<value>False</value>
</setting>
<setting name="Digging_PrimarySortOrder" serializeAs="String">
<value>0</value>
</setting>
<setting name="Reorganizing_RegionsIncludeAccessLevel" serializeAs="String">
<value>True</value>
</setting>
<setting name="General_Font" serializeAs="String">
<value>Consolas</value>
</setting>
<setting name="Reorganizing_ReverseOrderByAccessLevel" serializeAs="String">
<value>False</value>
</setting>
<setting name="Reorganizing_ExplicitMembersAtEnd" serializeAs="String">
<value>True</value>
</setting>
<setting name="General_Theme" serializeAs="String">
<value>1</value>
</setting>
<setting name="Cleaning_AutoSaveAndCloseIfOpenedByCleanup" serializeAs="String">
<value>False</value>
</setting>
<setting name="Collapsing_CollapseSolutionWhenOpened" serializeAs="String">
<value>False</value>
</setting>
<setting name="Collapsing_KeepSoloProjectExpanded" serializeAs="String">
<value>False</value>
</setting>
<setting name="Cleaning_RemoveEndOfFileTrailingNewLine" serializeAs="String">
<value>False</value>
</setting>
<setting name="Cleaning_InsertEndOfFileTrailingNewLine" serializeAs="String">
<value>True</value>
</setting>
<setting name="Cleaning_InsertBlankLinePaddingBeforePropertiesSingleLine"
serializeAs="String">
<value>True</value>
</setting>
<setting name="Cleaning_InsertBlankLinePaddingBeforeFieldsSingleLine"
serializeAs="String">
<value>True</value>
</setting>
<setting name="Feature_SettingCleanupOnSave" serializeAs="String">
<value>True</value>
</setting>
<setting name="Finding_TemporarilyOpenSolutionFolders" serializeAs="String">
<value>False</value>
</setting>
<setting name="Progressing_HideBuildProgressOnBuildStop" serializeAs="String">
<value>True</value>
</setting>
<setting name="Feature_FindInSolutionExplorer" serializeAs="String">
<value>False</value>
</setting>
</SteveCadwallader.CodeMaid.Properties.Settings>
</userSettings>
</configuration>
2 changes: 1 addition & 1 deletion Cuture.Extensions.SystemTextJson.Dynamic.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.4.33122.133
Expand All @@ -19,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution items", "solution
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
global.json = global.json
LICENSE.txt = LICENSE.txt
README.md = README.md
EndProjectSection
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,17 @@ IEnumerable<KeyValuePair<string, dynamic?>> enumerable = json;
//通过IDynamicKeyValueEnumerable
var enumerable = ((IDynamicKeyValueEnumerable)json).AsEnumerable();
```

## Index && Range

支持使用 Index 和 Range 语法访问数组

Note: 当前使用 Range 语法将会创建新的对象,对其修改不会反映到原始对象

```C#
//Index
var value = json.Array[^1];

//Range
var items = json.Array[..1];
```
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Cuture.Extensions.SystemTextJson.Dynamic;
[SimpleJob(RuntimeMoniker.Net472)]
[SimpleJob(RuntimeMoniker.Net60)]
[SimpleJob(RuntimeMoniker.Net70)]
[SimpleJob(RuntimeMoniker.Net80)]
[MemoryDiagnoser]
public class GenericBenchmark
{
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"msbuild-sdks": {
"MSTest.Sdk": "3.5.0"
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net7.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>

<IsPackable>true</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand All @@ -10,15 +10,15 @@
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />

<PackageReference Include="System.Text.Json" Version="7.0.1" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>

<ItemGroup>
<None Include="..\..\README.md" Link="README.md" Pack="true" PackagePath="/" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.*" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.*" PrivateAssets="All" />
</ItemGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
Expand All @@ -36,7 +36,7 @@

<Authors>Stratos</Authors>

<Version>1.0.2</Version>
<Version>1.0.3</Version>

<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/cuture/Cuture.Extensions.SystemTextJson.Dynamic</PackageProjectUrl>
Expand Down
47 changes: 37 additions & 10 deletions src/Cuture.Extensions.SystemTextJson.Dynamic/JSON.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ public static class JSON
{
#region Internal 字段

internal static readonly JsonSerializerOptions s_defaultJsonSerializerOptions = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
AllowTrailingCommas = true,
PropertyNameCaseInsensitive = false,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
IncludeFields = true,
ReadCommentHandling = JsonCommentHandling.Skip,
UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode,
};
internal static readonly JsonDocumentOptions s_defaultJsonDocumentOptions;

internal static readonly JsonNodeOptions s_defaultJsonNodeOptions;

internal static readonly JsonSerializerOptions s_defaultJsonSerializerOptions;

internal static readonly JsonWriterOptions s_defaultJsonWriterOptions;

#endregion Internal 字段

Expand All @@ -37,8 +34,38 @@ public static class JSON

static JSON()
{
s_defaultJsonSerializerOptions = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
AllowTrailingCommas = true,
PropertyNameCaseInsensitive = false,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
IncludeFields = true,
ReadCommentHandling = JsonCommentHandling.Skip,
UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode,
};

s_defaultJsonSerializerOptions.Converters.Add(new DynamicJsonConverter<JsonObjectDynamicAccessor>());
s_defaultJsonSerializerOptions.Converters.Add(new DynamicJsonConverter<JsonArrayDynamicAccessor>());

s_defaultJsonDocumentOptions = new JsonDocumentOptions()
{
AllowTrailingCommas = s_defaultJsonSerializerOptions.AllowTrailingCommas,
CommentHandling = s_defaultJsonSerializerOptions.ReadCommentHandling,
MaxDepth = s_defaultJsonSerializerOptions.MaxDepth,
};

s_defaultJsonWriterOptions = new JsonWriterOptions()
{
Encoder = s_defaultJsonSerializerOptions.Encoder,
Indented = s_defaultJsonSerializerOptions.WriteIndented,
SkipValidation = false,
};

s_defaultJsonNodeOptions = new JsonNodeOptions()
{
PropertyNameCaseInsensitive = s_defaultJsonSerializerOptions.PropertyNameCaseInsensitive,
};
}

#endregion Public 构造函数
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,63 @@ public override bool TryConvert(ConvertBinder binder, out object? result)

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object? result)
{
var index = GetIndex(indexes);
//TODO 多维数组
if (indexes.Length > 1)
{
throw new ArgumentOutOfRangeException(nameof(indexes));
}

var index = indexes[0];

if (index is int intIndex)
{
result = JsonNodeUtil.GetNodeAccessValue(_jsonArray[intIndex]);
return true;
}

#if NET6_0_OR_GREATER

//低版本可以靠手动定义 System.Index 和 System.Range 类型进行兼容,但会污染命名空间,容易出现冲突
else if (index is Index systemIndex)
{
result = JsonNodeUtil.GetNodeAccessValue(_jsonArray[systemIndex.GetOffset(_jsonArray.Count)]);
return true;
}
else if (index is Range systemRange)
{
var (offset, length) = systemRange.GetOffsetAndLength(_jsonArray.Count);

//创建一个新的 Json 对象进行操作
//TODO 封装一个针对 JsonNode[] 的包装类,以不用创建新的 Json 对象
using var memoryStream = new MemoryStream();
using var writer = new Utf8JsonWriter(memoryStream, JSON.s_defaultJsonWriterOptions);

writer.WriteStartArray();

foreach (var item in _jsonArray.AsEnumerable().Skip(offset).Take(length))
{
if (item is null)
{
writer.WriteNullValue();
}
else
{
item.WriteTo(writer, JSON.s_defaultJsonSerializerOptions);
}
}

writer.WriteEndArray();

writer.Flush();

memoryStream.Seek(0, SeekOrigin.Begin);

result = JsonNodeUtil.GetNodeAccessValue(JsonNode.Parse(memoryStream, JSON.s_defaultJsonNodeOptions, JSON.s_defaultJsonDocumentOptions));

return true;
}

#endif
throw new ArgumentException($"not support for index {index}.");
}

Expand All @@ -76,7 +126,12 @@ public override bool TryGetMember(GetMemberBinder binder, out object? result)

public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value)
{
var index = GetIndex(indexes);
if (indexes.Length > 1)
{
throw new ArgumentOutOfRangeException(nameof(indexes));
}

var index = indexes[0];

if (index is int intIndex)
{
Expand All @@ -87,6 +142,15 @@ public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object
_jsonArray[intIndex] = JsonNode.Parse(JSON.stringify(value));
return true;
}
#if NET6_0_OR_GREATER

//低版本可以靠手动定义 System.Index 和 System.Range 类型进行兼容,但会污染命名空间,容易出现冲突
else if (index is Index systemIndex)
{
_jsonArray[systemIndex.GetOffset(_jsonArray.Count)] = JsonNode.Parse(JSON.stringify(value));
return true;
}
#endif

throw new ArgumentException($"not support for index {index}.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,17 @@ static bool Result(object? resultValue, out object? result)

#region Protected 方法

protected static object GetIndex(object[] indexes)
protected static string GetSingleStringIndex(object[] indexes)
{
if (indexes.Length > 1)
{
throw new ArgumentOutOfRangeException(nameof(indexes));
}

return indexes[0];
var index = indexes[0];

return index as string
?? index.ToString()!;
}

#endregion Protected 方法
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ public override bool TryConvert(ConvertBinder binder, out object? result)

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object? result)
{
var index = GetIndex(indexes);
var propertyName = index as string ?? index.ToString()!;
var propertyName = GetSingleStringIndex(indexes);

return TryGetMember(propertyName, out result);
}
Expand All @@ -61,8 +60,7 @@ public override bool TryGetMember(GetMemberBinder binder, out object? result)

public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value)
{
var index = GetIndex(indexes);
var propertyName = index as string ?? index.ToString()!;
var propertyName = GetSingleStringIndex(indexes);

SetProperty(propertyName, value);
return true;
Expand Down
Loading

0 comments on commit ba8607a

Please sign in to comment.