问题
I need to serialize and deserialize an object that contains an System.Uri property using the latest (4.0.3) Json.NET library.
The following code demonstrates the problem:
string input = "http://test.com/%22foo+bar%22";
Uri uri = new Uri(input);
string json = JsonConvert.SerializeObject(uri);
Uri output = JsonConvert.DeserializeObject<Uri>(json);
The DeserializeObject method throws an JsonReaderException. This works fine with 4.0.2.
I've submitted an issue on codeplex with tests and patch to fix the issue, but it seems it takes a bit for the author to release a fixed version.
Meanwhile is there anything i can do ( using JsonSettings or anything else ) to make the latest version work as expected?
A few options i have so far:
- stick to 4.0.2 - new nuget packages depend on 4.0.3
- change uri to string - i rather go with option 1 and manually managed pkg dependencies
- use custom build with patch applied - this is what i'm doing now but i hate the idea of overriding the nuget package's assemblies.
回答1:
You can write a helper class that circumvents the usual handling of System.Uri
and treats it purely as a string, using the Uri.OriginalString
property.
Here's a code sample that does exactly that with a converter class derived from Json.Net's JsonConverter
.
OriginalString Property (System.Uri) @ MSDN
One caveat is that you must update all places where you use JsonConvert to include the helper class as one of the extra JsonConverter
parameters.
I've also added an example using Uri as a member variable in a class, to demonstrate that one wouldn't necessarily have to override the attributes on a class, though it might be more convenient for you. If so, you could use [JsonConverter(UriConverter)]
as an attribute on members that need it.
using Newtonsoft.Json;
namespace JsonUriSerializeTest
{
class Program
{
public class UriConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.Equals(typeof(Uri));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.String)
{
return new Uri((string)reader.Value);
}
if (reader.TokenType == JsonToken.Null)
{
return null;
}
throw new InvalidOperationException("Unhandled case for UriConverter. Check to see if this converter has been applied to the wrong serialization type.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (null == value)
{
writer.WriteNull();
return;
}
if (value is Uri)
{
writer.WriteValue(((Uri)value).OriginalString);
return;
}
throw new InvalidOperationException("Unhandled case for UriConverter. Check to see if this converter has been applied to the wrong serialization type.");
}
}
public class UriPair
{
public string label { get; set; }
public Uri first { get; set; }
public Uri second { get; set; }
public void Display()
{
Console.WriteLine(string.Format("label: {0}", label));
Console.WriteLine(string.Format("first: {0}", first));
Console.WriteLine(string.Format("second: {0}", second));
}
}
static void Main(string[] args)
{
string input = "http://test.com/%22foo+bar%22";
Uri uri = new Uri(input);
string json = JsonConvert.SerializeObject(uri, new UriConverter());
Uri output = JsonConvert.DeserializeObject<Uri>(json, new UriConverter());
Console.WriteLine(input);
Console.WriteLine(output.ToString());
Console.WriteLine();
UriPair pair = new UriPair();
pair.label = input;
pair.first = null;
pair.second = new Uri(input);
string jsonPair = JsonConvert.SerializeObject(pair, new UriConverter());
UriPair outputPair = JsonConvert.DeserializeObject<UriPair>(jsonPair, new UriConverter());
outputPair.Display();
Console.WriteLine();
Console.ReadKey();
}
}
}
来源:https://stackoverflow.com/questions/7792382/json-net-uri-deserialization-errors