Skip to content

Commit aa982be

Browse files
authored
perf: Optimize xrefmap.json file deserialization performance (#9824)
perf: optimize xrefmap.json file deserialization
1 parent 2799e6f commit aa982be

File tree

6 files changed

+140
-8
lines changed

6 files changed

+140
-8
lines changed

src/Docfx.Build/XRefMaps/XRefMapDownloader.cs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,25 @@ protected static IXRefContainer DownloadFromLocal(Uri uri)
111111
private static IXRefContainer ReadLocalFile(string filePath)
112112
{
113113
Logger.LogVerbose($"Reading from file: {filePath}");
114-
if (".zip".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase))
114+
115+
switch (Path.GetExtension(filePath).ToLowerInvariant())
115116
{
116-
return XRefArchive.Open(filePath, XRefArchiveMode.Read);
117-
}
117+
case ".zip":
118+
return XRefArchive.Open(filePath, XRefArchiveMode.Read);
118119

119-
using var sr = File.OpenText(filePath);
120-
return YamlUtility.Deserialize<XRefMap>(sr);
120+
case ".json":
121+
{
122+
using var stream = File.OpenText(filePath);
123+
return JsonUtility.Deserialize<XRefMap>(stream);
124+
}
125+
126+
case ".yml":
127+
default:
128+
{
129+
using var sr = File.OpenText(filePath);
130+
return YamlUtility.Deserialize<XRefMap>(sr);
131+
}
132+
}
121133
}
122134

123135
protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
@@ -134,10 +146,21 @@ protected static async Task<XRefMap> DownloadFromWebAsync(Uri uri)
134146
};
135147

136148
using var stream = await httpClient.GetStreamAsync(uri);
137-
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
138-
var map = YamlUtility.Deserialize<XRefMap>(sr);
139149

140-
return map;
150+
switch (Path.GetExtension(uri.AbsolutePath).ToLowerInvariant())
151+
{
152+
case ".json":
153+
{
154+
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
155+
return JsonUtility.Deserialize<XRefMap>(sr);
156+
}
157+
case ".yml":
158+
default:
159+
{
160+
using var sr = new StreamReader(stream, bufferSize: 81920); // Default :1024 byte
161+
return YamlUtility.Deserialize<XRefMap>(sr);
162+
}
163+
}
141164
}
142165

143166
public static void UpdateHref(XRefMap map, Uri uri)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Text.Json.Serialization;
5+
using Newtonsoft.Json;
46
using YamlDotNet.Serialization;
57

68
namespace Docfx.Build.Engine;
79

810
public class XRefMapRedirection
911
{
1012
[YamlMember(Alias = "uidPrefix")]
13+
[JsonProperty("uidPrefix")]
14+
[JsonPropertyName("uidPrefix")]
1115
public string UidPrefix { get; set; }
1216

1317
[YamlMember(Alias = "href")]
18+
[JsonProperty("Href")]
19+
[JsonPropertyName("href")]
1420
public string Href { get; set; }
1521
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"references": [
3+
{
4+
"fullName": "str",
5+
"href": "https://docs.python.org/3.5/library/stdtypes.html#str",
6+
"name": "str",
7+
"uid": "str"
8+
}
9+
]
10+
}

test/Docfx.Build.Tests/XRefMapDownloaderTest.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Net;
5+
using FluentAssertions;
56
using Xunit;
67

78
namespace Docfx.Build.Engine.Tests;
@@ -35,4 +36,18 @@ public async Task ReadLocalXRefMapWithFallback()
3536
Assert.NotNull(xrefSpec);
3637
Assert.Equal("https://docs.python.org/3.5/library/stdtypes.html#str", xrefSpec.Href);
3738
}
39+
40+
[Fact]
41+
public async Task ReadLocalXRefMapJsonFileTest()
42+
{
43+
// Arrange
44+
var path = Path.Combine(Directory.GetCurrentDirectory(), "TestData", "xrefmap.json");
45+
46+
XRefMapDownloader downloader = new XRefMapDownloader();
47+
var xrefMap = await downloader.DownloadAsync(new Uri(path)) as XRefMap;
48+
49+
// Assert
50+
xrefMap.Should().NotBeNull();
51+
xrefMap.References.Should().HaveCount(1);
52+
}
3853
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Text;
5+
using Docfx.Common;
6+
using Docfx.Plugins;
7+
using FluentAssertions;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
11+
namespace Docfx.Build.Engine.Tests;
12+
13+
public class XRefMapSerializationTest
14+
{
15+
[Fact]
16+
public void XRefMapSerializationRoundTripTest()
17+
{
18+
var model = new XRefMap
19+
{
20+
BaseUrl = "http://localhost",
21+
Sorted = true,
22+
HrefUpdated = null,
23+
Redirections = new List<XRefMapRedirection>
24+
{
25+
new XRefMapRedirection
26+
{
27+
Href = "Dummy",
28+
UidPrefix = "Dummy"
29+
},
30+
},
31+
References = new List<XRefSpec>
32+
{
33+
new XRefSpec(new Dictionary<string,object>
34+
{
35+
["Additional1"] = "Dummy",
36+
})
37+
{
38+
Uid = "Dummy",
39+
Name = "Dummy",
40+
Href = "Dummy",
41+
CommentId ="Dummy",
42+
IsSpec = true,
43+
},
44+
},
45+
Others = new Dictionary<string, object>
46+
{
47+
["Other1"] = "Dummy",
48+
}
49+
};
50+
51+
// Arrange
52+
var jsonResult = RoundtripByNewtonsoftJson(model);
53+
var yamlResult = RoundtripWithYamlDotNet(model);
54+
55+
// Assert
56+
jsonResult.Should().BeEquivalentTo(model);
57+
yamlResult.Should().BeEquivalentTo(model);
58+
}
59+
60+
private static T RoundtripByNewtonsoftJson<T>(T model)
61+
{
62+
var json = JsonUtility.Serialize(model);
63+
return JsonUtility.Deserialize<T>(new StringReader(json));
64+
}
65+
66+
private static T RoundtripWithYamlDotNet<T>(T model)
67+
{
68+
var sb = new StringBuilder();
69+
using var sw = new StringWriter(sb);
70+
YamlUtility.Serialize(sw, model);
71+
var json = sb.ToString();
72+
return YamlUtility.Deserialize<T>(new StringReader(json));
73+
}
74+
}

test/docfx.Tests/Api.verified.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,8 +486,12 @@ protected override Docfx.Build.Engine.IXRefContainer GetMap(string name) { }
486486
public class XRefMapRedirection
487487
{
488488
public XRefMapRedirection() { }
489+
[Newtonsoft.Json.JsonProperty("Href")]
490+
[System.Text.Json.Serialization.JsonPropertyName("href")]
489491
[YamlDotNet.Serialization.YamlMember(Alias="href")]
490492
public string Href { get; set; }
493+
[Newtonsoft.Json.JsonProperty("uidPrefix")]
494+
[System.Text.Json.Serialization.JsonPropertyName("uidPrefix")]
491495
[YamlDotNet.Serialization.YamlMember(Alias="uidPrefix")]
492496
public string UidPrefix { get; set; }
493497
}

0 commit comments

Comments
 (0)