Skip to content

Commit ba8607a

Browse files
committed
feat: JSON数组访问支持索引和范围运算符
1 parent 95e9523 commit ba8607a

File tree

12 files changed

+330
-32
lines changed

12 files changed

+330
-32
lines changed

CodeMaid.config

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration>
3+
<configSections>
4+
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
5+
<section name="SteveCadwallader.CodeMaid.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
6+
</sectionGroup>
7+
</configSections>
8+
<userSettings>
9+
<SteveCadwallader.CodeMaid.Properties.Settings>
10+
<setting name="Reorganizing_MemberTypeFields" serializeAs="String">
11+
<value>Fields||3||字段</value>
12+
</setting>
13+
<setting name="Reorganizing_MemberTypeProperties" serializeAs="String">
14+
<value>Properties||4||属性</value>
15+
</setting>
16+
<setting name="Reorganizing_MemberTypeMethods" serializeAs="String">
17+
<value>Methods||9||方法</value>
18+
</setting>
19+
<setting name="Reorganizing_MemberTypeDelegates" serializeAs="String">
20+
<value>Delegates||1||委托</value>
21+
</setting>
22+
<setting name="Reorganizing_MemberTypeEvents" serializeAs="String">
23+
<value>Events||2||事件</value>
24+
</setting>
25+
<setting name="Reorganizing_MemberTypeInterfaces" serializeAs="String">
26+
<value>Interfaces||8||接口</value>
27+
</setting>
28+
<setting name="Reorganizing_MemberTypeConstructors" serializeAs="String">
29+
<value>Constructors||6||构造函数</value>
30+
</setting>
31+
<setting name="Reorganizing_MemberTypeEnums" serializeAs="String">
32+
<value>Enums||10||枚举</value>
33+
</setting>
34+
<setting name="Reorganizing_MemberTypeDestructors" serializeAs="String">
35+
<value>Destructors||7||析构函数</value>
36+
</setting>
37+
<setting name="Reorganizing_MemberTypeIndexers" serializeAs="String">
38+
<value>Indexers||5||索引器</value>
39+
</setting>
40+
<setting name="Reorganizing_MemberTypeClasses" serializeAs="String">
41+
<value>Classes||12||类</value>
42+
</setting>
43+
<setting name="Reorganizing_MemberTypeStructs" serializeAs="String">
44+
<value>Structs||11||结构体</value>
45+
</setting>
46+
<setting name="Reorganizing_RegionsInsertNewRegions" serializeAs="String">
47+
<value>True</value>
48+
</setting>
49+
<setting name="Reorganizing_RunAtStartOfCleanup" serializeAs="String">
50+
<value>True</value>
51+
</setting>
52+
<setting name="Reorganizing_RegionsRemoveExistingRegions" serializeAs="String">
53+
<value>False</value>
54+
</setting>
55+
<setting name="Digging_PrimarySortOrder" serializeAs="String">
56+
<value>0</value>
57+
</setting>
58+
<setting name="Reorganizing_RegionsIncludeAccessLevel" serializeAs="String">
59+
<value>True</value>
60+
</setting>
61+
<setting name="General_Font" serializeAs="String">
62+
<value>Consolas</value>
63+
</setting>
64+
<setting name="Reorganizing_ReverseOrderByAccessLevel" serializeAs="String">
65+
<value>False</value>
66+
</setting>
67+
<setting name="Reorganizing_ExplicitMembersAtEnd" serializeAs="String">
68+
<value>True</value>
69+
</setting>
70+
<setting name="General_Theme" serializeAs="String">
71+
<value>1</value>
72+
</setting>
73+
<setting name="Cleaning_AutoSaveAndCloseIfOpenedByCleanup" serializeAs="String">
74+
<value>False</value>
75+
</setting>
76+
<setting name="Collapsing_CollapseSolutionWhenOpened" serializeAs="String">
77+
<value>False</value>
78+
</setting>
79+
<setting name="Collapsing_KeepSoloProjectExpanded" serializeAs="String">
80+
<value>False</value>
81+
</setting>
82+
<setting name="Cleaning_RemoveEndOfFileTrailingNewLine" serializeAs="String">
83+
<value>False</value>
84+
</setting>
85+
<setting name="Cleaning_InsertEndOfFileTrailingNewLine" serializeAs="String">
86+
<value>True</value>
87+
</setting>
88+
<setting name="Cleaning_InsertBlankLinePaddingBeforePropertiesSingleLine"
89+
serializeAs="String">
90+
<value>True</value>
91+
</setting>
92+
<setting name="Cleaning_InsertBlankLinePaddingBeforeFieldsSingleLine"
93+
serializeAs="String">
94+
<value>True</value>
95+
</setting>
96+
<setting name="Feature_SettingCleanupOnSave" serializeAs="String">
97+
<value>True</value>
98+
</setting>
99+
<setting name="Finding_TemporarilyOpenSolutionFolders" serializeAs="String">
100+
<value>False</value>
101+
</setting>
102+
<setting name="Progressing_HideBuildProgressOnBuildStop" serializeAs="String">
103+
<value>True</value>
104+
</setting>
105+
<setting name="Feature_FindInSolutionExplorer" serializeAs="String">
106+
<value>False</value>
107+
</setting>
108+
</SteveCadwallader.CodeMaid.Properties.Settings>
109+
</userSettings>
110+
</configuration>

Cuture.Extensions.SystemTextJson.Dynamic.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
Microsoft Visual Studio Solution File, Format Version 12.00
32
# Visual Studio Version 17
43
VisualStudioVersion = 17.4.33122.133
@@ -19,6 +18,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "solution items", "solution
1918
ProjectSection(SolutionItems) = preProject
2019
.editorconfig = .editorconfig
2120
Directory.Build.props = Directory.Build.props
21+
global.json = global.json
2222
LICENSE.txt = LICENSE.txt
2323
README.md = README.md
2424
EndProjectSection

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,17 @@ IEnumerable<KeyValuePair<string, dynamic?>> enumerable = json;
105105
//通过IDynamicKeyValueEnumerable
106106
var enumerable = ((IDynamicKeyValueEnumerable)json).AsEnumerable();
107107
```
108+
109+
## Index && Range
110+
111+
支持使用 Index 和 Range 语法访问数组
112+
113+
Note: 当前使用 Range 语法将会创建新的对象,对其修改不会反映到原始对象
114+
115+
```C#
116+
//Index
117+
var value = json.Array[^1];
118+
119+
//Range
120+
var items = json.Array[..1];
121+
```

benchmark/Cuture.Extensions.SystemTextJson.Dynamic.Benchmark/GenericBenchmark.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace Cuture.Extensions.SystemTextJson.Dynamic;
88
[SimpleJob(RuntimeMoniker.Net472)]
99
[SimpleJob(RuntimeMoniker.Net60)]
1010
[SimpleJob(RuntimeMoniker.Net70)]
11+
[SimpleJob(RuntimeMoniker.Net80)]
1112
[MemoryDiagnoser]
1213
public class GenericBenchmark
1314
{

global.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"msbuild-sdks": {
3+
"MSTest.Sdk": "3.5.0"
4+
}
5+
}

src/Cuture.Extensions.SystemTextJson.Dynamic/Cuture.Extensions.SystemTextJson.Dynamic.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net6.0;net7.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;net6.0;net7.0;net8.0</TargetFrameworks>
55

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

13-
<PackageReference Include="System.Text.Json" Version="7.0.1" />
13+
<PackageReference Include="System.Text.Json" Version="8.0.4" />
1414
</ItemGroup>
1515

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

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

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

3737
<Authors>Stratos</Authors>
3838

39-
<Version>1.0.2</Version>
39+
<Version>1.0.3</Version>
4040

4141
<PackageLicenseExpression>MIT</PackageLicenseExpression>
4242
<PackageProjectUrl>https://github.com/cuture/Cuture.Extensions.SystemTextJson.Dynamic</PackageProjectUrl>

src/Cuture.Extensions.SystemTextJson.Dynamic/JSON.cs

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@ public static class JSON
1313
{
1414
#region Internal 字段
1515

16-
internal static readonly JsonSerializerOptions s_defaultJsonSerializerOptions = new()
17-
{
18-
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
19-
AllowTrailingCommas = true,
20-
PropertyNameCaseInsensitive = false,
21-
NumberHandling = JsonNumberHandling.AllowReadingFromString,
22-
IncludeFields = true,
23-
ReadCommentHandling = JsonCommentHandling.Skip,
24-
UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode,
25-
};
16+
internal static readonly JsonDocumentOptions s_defaultJsonDocumentOptions;
17+
18+
internal static readonly JsonNodeOptions s_defaultJsonNodeOptions;
19+
20+
internal static readonly JsonSerializerOptions s_defaultJsonSerializerOptions;
21+
22+
internal static readonly JsonWriterOptions s_defaultJsonWriterOptions;
2623

2724
#endregion Internal 字段
2825

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

3835
static JSON()
3936
{
37+
s_defaultJsonSerializerOptions = new()
38+
{
39+
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
40+
AllowTrailingCommas = true,
41+
PropertyNameCaseInsensitive = false,
42+
NumberHandling = JsonNumberHandling.AllowReadingFromString,
43+
IncludeFields = true,
44+
ReadCommentHandling = JsonCommentHandling.Skip,
45+
UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode,
46+
};
47+
4048
s_defaultJsonSerializerOptions.Converters.Add(new DynamicJsonConverter<JsonObjectDynamicAccessor>());
4149
s_defaultJsonSerializerOptions.Converters.Add(new DynamicJsonConverter<JsonArrayDynamicAccessor>());
50+
51+
s_defaultJsonDocumentOptions = new JsonDocumentOptions()
52+
{
53+
AllowTrailingCommas = s_defaultJsonSerializerOptions.AllowTrailingCommas,
54+
CommentHandling = s_defaultJsonSerializerOptions.ReadCommentHandling,
55+
MaxDepth = s_defaultJsonSerializerOptions.MaxDepth,
56+
};
57+
58+
s_defaultJsonWriterOptions = new JsonWriterOptions()
59+
{
60+
Encoder = s_defaultJsonSerializerOptions.Encoder,
61+
Indented = s_defaultJsonSerializerOptions.WriteIndented,
62+
SkipValidation = false,
63+
};
64+
65+
s_defaultJsonNodeOptions = new JsonNodeOptions()
66+
{
67+
PropertyNameCaseInsensitive = s_defaultJsonSerializerOptions.PropertyNameCaseInsensitive,
68+
};
4269
}
4370

4471
#endregion Public 构造函数

src/Cuture.Extensions.SystemTextJson.Dynamic/JsonArrayDynamicAccessor.cs

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,63 @@ public override bool TryConvert(ConvertBinder binder, out object? result)
5454

5555
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object? result)
5656
{
57-
var index = GetIndex(indexes);
57+
//TODO 多维数组
58+
if (indexes.Length > 1)
59+
{
60+
throw new ArgumentOutOfRangeException(nameof(indexes));
61+
}
62+
63+
var index = indexes[0];
64+
5865
if (index is int intIndex)
5966
{
6067
result = JsonNodeUtil.GetNodeAccessValue(_jsonArray[intIndex]);
6168
return true;
6269
}
6370

71+
#if NET6_0_OR_GREATER
72+
73+
//低版本可以靠手动定义 System.Index 和 System.Range 类型进行兼容,但会污染命名空间,容易出现冲突
74+
else if (index is Index systemIndex)
75+
{
76+
result = JsonNodeUtil.GetNodeAccessValue(_jsonArray[systemIndex.GetOffset(_jsonArray.Count)]);
77+
return true;
78+
}
79+
else if (index is Range systemRange)
80+
{
81+
var (offset, length) = systemRange.GetOffsetAndLength(_jsonArray.Count);
82+
83+
//创建一个新的 Json 对象进行操作
84+
//TODO 封装一个针对 JsonNode[] 的包装类,以不用创建新的 Json 对象
85+
using var memoryStream = new MemoryStream();
86+
using var writer = new Utf8JsonWriter(memoryStream, JSON.s_defaultJsonWriterOptions);
87+
88+
writer.WriteStartArray();
89+
90+
foreach (var item in _jsonArray.AsEnumerable().Skip(offset).Take(length))
91+
{
92+
if (item is null)
93+
{
94+
writer.WriteNullValue();
95+
}
96+
else
97+
{
98+
item.WriteTo(writer, JSON.s_defaultJsonSerializerOptions);
99+
}
100+
}
101+
102+
writer.WriteEndArray();
103+
104+
writer.Flush();
105+
106+
memoryStream.Seek(0, SeekOrigin.Begin);
107+
108+
result = JsonNodeUtil.GetNodeAccessValue(JsonNode.Parse(memoryStream, JSON.s_defaultJsonNodeOptions, JSON.s_defaultJsonDocumentOptions));
109+
110+
return true;
111+
}
112+
113+
#endif
64114
throw new ArgumentException($"not support for index {index}.");
65115
}
66116

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

77127
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value)
78128
{
79-
var index = GetIndex(indexes);
129+
if (indexes.Length > 1)
130+
{
131+
throw new ArgumentOutOfRangeException(nameof(indexes));
132+
}
133+
134+
var index = indexes[0];
80135

81136
if (index is int intIndex)
82137
{
@@ -87,6 +142,15 @@ public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object
87142
_jsonArray[intIndex] = JsonNode.Parse(JSON.stringify(value));
88143
return true;
89144
}
145+
#if NET6_0_OR_GREATER
146+
147+
//低版本可以靠手动定义 System.Index 和 System.Range 类型进行兼容,但会污染命名空间,容易出现冲突
148+
else if (index is Index systemIndex)
149+
{
150+
_jsonArray[systemIndex.GetOffset(_jsonArray.Count)] = JsonNode.Parse(JSON.stringify(value));
151+
return true;
152+
}
153+
#endif
90154

91155
throw new ArgumentException($"not support for index {index}.");
92156
}

src/Cuture.Extensions.SystemTextJson.Dynamic/JsonDynamicAccessor.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,17 @@ static bool Result(object? resultValue, out object? result)
9797

9898
#region Protected 方法
9999

100-
protected static object GetIndex(object[] indexes)
100+
protected static string GetSingleStringIndex(object[] indexes)
101101
{
102102
if (indexes.Length > 1)
103103
{
104104
throw new ArgumentOutOfRangeException(nameof(indexes));
105105
}
106106

107-
return indexes[0];
107+
var index = indexes[0];
108+
109+
return index as string
110+
?? index.ToString()!;
108111
}
109112

110113
#endregion Protected 方法

src/Cuture.Extensions.SystemTextJson.Dynamic/JsonObjectDynamicAccessor.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ public override bool TryConvert(ConvertBinder binder, out object? result)
4444

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

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

6261
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object? value)
6362
{
64-
var index = GetIndex(indexes);
65-
var propertyName = index as string ?? index.ToString()!;
63+
var propertyName = GetSingleStringIndex(indexes);
6664

6765
SetProperty(propertyName, value);
6866
return true;

0 commit comments

Comments
 (0)