3 回答
TA贡献1874条经验 获得超12个赞
.ToDictionary
要求每一个key
都是独一无二的,通过设计。Linq 没有
.DistinctBy( x => x.y )
方法,但我们可以使用.GroupBy( x => x.y ).Select( grp => grp.Last() )
. 这具有丢弃具有相同值的所有先前结果的效果y
。
因此,如果您首先按 HTTP 标头名称分组,然后选择每个组中的最后一项,那么这将得到您想要的:
// Using cached static fields to avoid unnecessary array allocation:
static readonly String[] _splitOnLines = new String[] { "\r\n" };
static readonly String[] _splitHeader = new String[] { ": " };
public static Dictionary<String,String> CreateLookupDictionary(String input)
{
Debug.WriteLine(input);
return input
.Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )
.Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )
.Where( arr => arr.Length == 2 ) // filter out invalid lines, if any
.Select( arr => ( name: arr[0], value: arr[1] ) ) // using C# 7 named tuples for maintainability
.GroupBy( header => header.name )
.Select( duplicateHeaderGroup => duplicateHeaderGroup.Last() )
.ToDictionary( header => header.name, header.value, StringComparer.InvariantCultureIgnoreCase );
}
或者,使用自定义聚合,该聚合使用始终成功的键索引Item设置器属性。如果与我之前的示例相比很少有重复项,则此方法可能具有更快的性能。
public static Dictionary<String,String> CreateLookupDictionary(String input)
{
Debug.WriteLine(input);
return input
.Split( _splitOnLines , StringSplitOptions.RemoveEmptyEntries )
.Select( line => line.Split( _splitHeader, StringSplitOptions.None ) )
.Where( arr => arr.Length == 2 )
.Select( arr => ( name: arr[0], value: arr[1] ) )
.Aggregate(
new Dictionary<String,String>( StringComparer.InvariantCultureIgnoreCase ),
( d, header ) =>
{
d[ header.name ] = header.value;
return d;
}
);
}
TA贡献1804条经验 获得超3个赞
.Net Core 2.0 有解决办法:
使用 Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpParser 类:
如果您使用这种方法,您将能够处理许多无法预料的边缘情况。
这是伪代码(未经测试)——这只适用于 .Net Core(显然)
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Text;
public class ExampleUsage
{
public static void Main(string[] args)
{
string requestString =
@"POST /resource/?query_id=0 HTTP/1.1
Host: example.com
User-Agent: custom
Accept: */*
Connection: close
Content-Length: 20
Content-Type: application/json
{""key1"":1, ""key2"":2}";
var headerResult = Parser.Parse(requestString);
}
}
public class Parser : IHttpHeadersHandler
{
private Dictionary<string, string> result = null;
public Dictionary<string, string> Parse(string requestString)
{
result = new Dictionary<string, string>();
byte[] requestRaw = Encoding.UTF8.GetBytes(requestString);
ReadOnlySequence<byte> buffer = new ReadOnlySequence<byte>(requestRaw);
HttpParser<Program> parser = new HttpParser<Program>();
parser.ParseRequestLine(this, buffer, out var consumed, out var examined);
buffer = buffer.Slice(consumed);
parser.ParseHeaders(this, buffer, out consumed, out examined, out var b);
buffer = buffer.Slice(consumed);
}
public void OnHeader(Span<byte> name, Span<byte> value)
{
result.Add(Encoding.UTF8.GetString(name), Encoding.UTF8.GetString(value));
}
}
TA贡献1818条经验 获得超11个赞
就我个人而言,我会使用它.ToLookup来保留同一个键的多个值。 .ToLookup不会在重复键上出错,它会创建一个IEnumerable<V>值,这种情况是IEnumerable<string>:
public static ILookup<string, string> CreateLookup(string input)
{
Debug.WriteLine(input);
return input.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(new string[] {": "}, StringSplitOptions.None))
.ToLookup(x => x[0], x => x[1], StringComparer.InvariantCultureIgnoreCase);
}
- 3 回答
- 0 关注
- 161 浏览
添加回答
举报