-
Notifications
You must be signed in to change notification settings - Fork 305
/
BinaryAsHexJsonConverter.cs
106 lines (91 loc) · 3.67 KB
/
BinaryAsHexJsonConverter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// <copyright file="BinaryAsHexJsonConverter.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.Persistence.EntityFramework.Json
{
using System;
using Newtonsoft.Json;
/// <summary>
/// This converter converts a hex string to a byte array.
/// Newtonsoft.Json expects a base64 string, but postgres delivers us a hex string,
/// so this converter is needed.
/// </summary>
public class BinaryAsHexJsonConverter : JsonConverter
{
/// <summary>
/// The prefix of a byte array string, provided by postgres.
/// </summary>
private const string ByteArrayPrefix = @"\\x";
/// <summary>
/// A character to value mapping table for faster hex string parsing. Each character (0-9a-fA-F)
/// has it's corresponding value of 0-15. All other characters are initialized with <see cref="int.MaxValue"/>.
/// </summary>
private readonly int[] characterToValue;
/// <summary>
/// Initializes a new instance of the <see cref="BinaryAsHexJsonConverter"/> class.
/// </summary>
public BinaryAsHexJsonConverter()
{
this.characterToValue = new int[byte.MaxValue + 1];
for (int i = 0; i < this.characterToValue.Length; i++)
{
this.characterToValue[i] = int.MaxValue;
}
for (byte i = 0; i < 10; i++)
{
this.characterToValue['0' + i] = i;
}
for (byte i = 0xA; i < 16; i++)
{
this.characterToValue['a' + i - 0xA] = i;
this.characterToValue['A' + i - 0xA] = i;
}
}
/// <inheritdoc />
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var array = value as byte[];
if (array == null)
{
writer.WriteNull();
return;
}
var arrayString = ByteArrayPrefix + BitConverter.ToString(array).Replace("-", string.Empty).ToLowerInvariant();
writer.WriteValue(arrayString);
}
/// <inheritdoc />
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
if (reader.TokenType == JsonToken.String)
{
var prefixSize = ByteArrayPrefix.Length;
var value = (string)reader.Value;
var data = new byte[(value.Length - prefixSize) / 2];
for (var i = 0; i < data.Length; i++)
{
var index = prefixSize + (i * 2);
int highNibble = this.ParseCharacter(value[index]);
int lowNibble = this.ParseCharacter(value[index + 1]);
data[i] = (byte)((highNibble << 4) | lowNibble);
}
return data;
}
throw new JsonSerializationException($"Unexpected token parsing binary. Expected String, got {reader.TokenType}.");
}
/// <inheritdoc />
public override bool CanConvert(Type objectType) => objectType == typeof(byte[]);
private int ParseCharacter(char character)
{
var value = this.characterToValue[character];
if (value == int.MaxValue)
{
throw new Exception($"invalid character: {character}");
}
return value;
}
}
}