更新
我已完全重写了这个问题并包含了一个完整的工作示例。
我现在的简洁问题是:
为什么我没有看到字符串" \ u001d"的个别字符?当我使用RestSharp RestClient并选择json作为RequestFormat选项将一个简单的对象发送到服务器?我将输出发送到测试服务器,当我用二进制编辑器检查输出时,只看到1d。
我希望5C 75 30 30 31 64
(' \',' u'' 0',' 0',' 1',' d')如果您只是使用NewtonSoft.Json来序列化包含字符串" \ u001d"的同一个简单对象,那就是您所看到的。我的理解是,RestSharp会将您的对象序列化为.json(如果您选择相应的选项)并发送到服务器进行反序列化。
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Serializers;
using System;
using System.Collections.Generic;
using System.IO;
/// <summary>
/// To Test this,
/// 1. I first serialize a string to JSON and then print out the JSON string as a character array to show that
/// '\' 'u' '0' '0' '1' 'd' all show up in the serialized json string. I also put in original string \\001d (double \) for use later with RestSharp.
/// 2. I expect this json string to show up if I use RestSharp to send an object to a web service because RestSharp serializes everything to JSON
/// if it's so configured.
/// I make use of http://posttestserver.com/ to
/// act as "server", so I can just examine the data on that site. I copy the data on the server to a text file and open with binary editor.
/// Notice that there is just a '1d' present between 12 and 34.
/// You can perhaps duplicate what you get with just serialization by starting with (double \). But why should you need to do that?
/// Notice I try both RestClient's serializer and try to use NewtonSoft Serializer w/ RestClient.
/// 3. Finally I tried to send the serialized string with a client that had not been set up for JSON. Unfortunately, on server, it just shows up
/// as <String />. Not sure if it's me setting up client incorrectly or what's going on.
/// </summary>
class Program
{
static void Main(string[] args)
{
Tank tank1 = new Tank();
string string1 = "12\u001d34" + " " + "56\\u001d78" ; // you don't need double \\ with just serialization, but perhaps you need them with RestClient?
tank1.Description = string1;// new string(array1);
tank1.Id = 1;
// Show that we can serialize and each character of \u001d shows up as
JsonSerializerSettings settings = new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore };
string conversion1 = JsonConvert.SerializeObject(tank1, Formatting.Indented, settings);
Console.WriteLine("JSON serialized string (showing hidden characters");
foreach(char dummyChar in conversion1.ToCharArray())
{
Console.Write(dummyChar + " ");
//Console.Write(conversion1.ToCharArray()[i] + " ");
} Console.WriteLine();
// Demonstrate that straight RestClient doesn't work.
RestClient client1 = new RestClient("http://posttestserver.com/"); // a website that let's you push data at it.
var request = new RestRequest(Method.POST); // method is Method.POST
request.AddHeader("Content-Type", "application/json");
request.AddHeader("X-ACME-API-VERSION", "1");
request.Resource = "post.php?dir=David"; // Put in unique name that you can easily find at http://posttestserver.com/data/2016/
request.RequestFormat = DataFormat.Json;
request.AddBody(tank1);
var response = client1.Execute(request); // after this line, go examine http://posttestserver.com/data/2016/ and find your post
// copy text to text file and open with binary editor. I claim you will see a 1d but not / (47) or u (117) or 0 (48) i.e. just 1d but not \u001d
// now try RequestClient w/ json serializer....
request.JsonSerializer = new JsonSerializerNewtonSoft();
response = client1.Execute(request);
// Finally, try just sending the json serialized stuff with a RestClient that has NOT been set to json stufff
RestClient client3 = new RestClient("http://posttestserver.com/"); // a website that let's you push data at it.
request.Resource = "post.php?dir=David"; // Put in unique name that you can find at http://posttestserver.com/data/2016/
var request3 = new RestRequest(Method.PUT); // method is Method.PUT // not sure what to put here
//request3.AddHeader("Content-Type", "application/json"); // not sure what to use here if anything
request3.AddHeader("X-ACME-API-VERSION", "1");
request3.Resource = "post.php?dir=David"; // Put in unique name that you can find at http://posttestserver.com/data/2016/
request3.RequestFormat = DataFormat.Xml; // not sure what to use here
request3.AddBody(conversion1);
var response3 = client3.Execute(request3); // hard to evaluate. Shows up at test server as <String />
}
}
interface ITank
{
int Id { get; set; }
string Description { get; set; }
}
public class Tank : ITank
{
public Tank() { }
public string Description { get; set; }
public int Id { get; set; }
}
public class Tanks
{
public Tanks() { }
public IEnumerable<Tank> Group { get; set; }
}
public class JsonSerializerNewtonSoft : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonSerializerNewtonSoft()
{
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonSerializerNewtonSoft(Newtonsoft.Json.JsonSerializer serializer)
{
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj)
{
using (var stringWriter = new StringWriter())
{
using (var jsonTextWriter = new JsonTextWriter(stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
答案 0 :(得分:2)
首先,感谢您发布可用于重现问题的Minimal, Complete, and Verifiable example;这使得帮助更容易。
好的,这里发生了一些导致你看到的结果的事情。让我们一次看一个。
首先,您要创建一个这样的字符串:
string string1 = "12\u001d34" + " " + "56\\u001d78";
你使用的反斜杠数确实很重要,因为它在C#中具有与在JSON中相同的特殊含义。具体来说,C#中的符号\uxxxx
表示&#34;将带有4位十六进制数字(UTF-16)字符代码xxxx的Unicode字符插入字符串&#34;。相反,符号\\
表示&#34;在字符串&#34;中插入单个\
字符。因此,在字符串的第一部分中,您将插入单个字符0x001d
,即组分隔符控制字符。在字符串的第二部分中,您要插入六个字符:\
,u
,0
,0
,1
和d
。您可以通过一个简单的测试程序看到自己的区别,该程序将字符转储为十六进制数字:
public class Program
{
public static void Main()
{
DumpCharsAsHex("\u001d"); // inserts actual 0x001d character (Group Separator) into string
DumpCharsAsHex("\\u001d"); // inserts chars '\', 'u', '0', '0', '1', 'd' into string
}
private static void DumpCharsAsHex(string s)
{
if (s != null)
{
for (int i = 0; i < s.Length; i++)
{
int c = s[i];
Console.Write(c.ToString("X") + " ");
}
}
Console.WriteLine();
}
}
输出:
1D
5C 75 30 30 31 64
小提琴:https://dotnetfiddle.net/8tjIiX
其次,对于嵌入在字符串中的控制字符,Json.Net和SimpleJson(RestSharp内部序列化器)之间肯定存在不同的行为。 Json.Net识别控制字符并将它们转换为适当的JSON转义序列。 (此转换由JavaScriptUtils.WriteEscapedJavaScriptString
方法完成,后者又调用StringUtils.ToCharAsUnicode
。另一方面,RestSharp不进行此类转换,只是通过JSON传递不可见的控制字符。 (你可以在SimpleJson.EscapeToJavascriptString
中看到这一点。)
同样,一个简单的测试程序证明了差异:
public class Program
{
public static void Main()
{
Foo foo = new Foo { Bar = "\u001d" };
string json = Newtonsoft.Json.JsonConvert.SerializeObject(foo);
Console.WriteLine(json);
DumpCharsAsHex(json);
string json2 = RestSharp.SimpleJson.SerializeObject(foo);
Console.WriteLine(json2);
DumpCharsAsHex(json2);
}
private static void DumpCharsAsHex(string s)
{
if (s != null)
{
for (int i = 0; i < s.Length; i++)
{
int c = s[i];
Console.Write(c.ToString("X") + " ");
}
}
Console.WriteLine();
}
}
public class Foo
{
public string Bar { get; set; }
}
输出:
{"Bar":"\u001d"}
7B 22 42 61 72 22 3A 22 5C 75 30 30 31 64 22 7D
{"Bar":""}
7B 22 42 61 72 22 3A 22 1D 22 7D
小提琴:https://dotnetfiddle.net/caxZfq
正如您所看到的,在Json.Net生成的第一个JSON中,原始字符串中的控制字符被转换为JSON字符转义符号,巧合地看起来就像原始C#代码一样。当JSON在另一端反序列化时,它将被转换回控制字符。
在RestSharp生成的第二个JSON中,控制字符实际存在(1D
之间的22
),即使它在JSON输出中不可见。我应该注意到,根据JSON spec(强调我的)第9节,这绝对是不正确的行为:
字符串是用引号(U + 0022)包装的Unicode代码点序列。所有人物都可能是 放在引号中,但必须转义的字符除外:引号(U + 0022), 反向固相线(U + 005C),控制字符U + 0000至U + 001F 。
由于保留了控制字符,因此在通过电线传输期间或在另一端的反序列化期间,它可能会以不合需要的方式被破坏或重新解释。
第三,在您的代码中,您似乎正在尝试使用Json.Net作为RestSharp的替代序列化程序,但它似乎并没有对您的结果产生影响。原因是因为你的陈述无序。
你这样做:
var request = new RestRequest(Method.POST);
...
request.RequestFormat = DataFormat.Json;
request.AddBody(tank1);
request.JsonSerializer = new JsonSerializerNewtonSoft();
response = client1.Execute(request);
请注意,在为请求设置JsonSerializer之前,您正在调用AddBody
。 RestRequest.AddBody
是调用序列化程序获取JSON并将结果添加到请求主体的方法;这是RestClient.Execute
不完成的。因此,当您设置备用JSON序列化程序时,为时已晚 - 您已经使用内部序列化程序将JSON添加到请求主体,并且永远不会调用备用序列化程序。颠倒这两个语句的顺序,它应该按照你想要的方式工作。
var request = new RestRequest(Method.POST);
...
request.RequestFormat = DataFormat.Json;
request.JsonSerializer = new JsonSerializerNewtonSoft();
request.AddBody(tank1);
response = client1.Execute(request);
希望这是有道理的。