我有一个C#Asp。Net MVC(5.2.7)应用程序,支持WebApi 2。x目标。净额4.5。1.我正在试验F#,并在解决方案中添加了一个F#库项目。该web应用程序引用了F#库。
现在,我希望能够让C#WebApi控制器返回F#对象,并保存F#对象。我有麻烦序列化F#记录与选项字段。下面是代码:
C#WebApi控制器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using FsLib;
namespace TestWebApp.Controllers
{
[Route("api/v1/Test")]
public class TestController : ApiController
{
// GET api/<controller>
public IHttpActionResult Get()
{
return Json(Test.getPersons);
}
// GET api/<controller>/5
public string Get(int id)
{
return "value";
}
[AllowAnonymous]
// POST api/<controller>
public IHttpActionResult Post([FromBody] Test.Person value)
{
return Json(Test.doSomethingCrazy(value));
}
// PUT api/<controller>/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/<controller>/5
public void Delete(int id)
{
}
}
}
FsLib.fs:
namespace FsLib
open System.Web.Mvc
open Option
open Newtonsoft.Json
module Test =
[<CLIMutable>]
//[<JsonConverter(typeof<Newtonsoft.Json.Converters.IdiomaticDuConverter>)>]
type Person = {
Name: string;
Age: int;
[<JsonConverter(typeof<Newtonsoft.Json.FSharp.OptionConverter>)>]
Children: Option<int> }
let getPersons = [{Name="Scorpion King";Age=30; Children = Some 3} ; {Name = "Popeye"; Age = 40; Children = None}] |> List.toSeq
let doSomethingCrazy (person: Person) = {
Name = person.Name.ToUpper();
Age = person.Age + 2 ;
Children = if person.Children.IsNone then Some 1 else person.Children |> Option.map (fun v -> v + 1); }
let deserializePerson (str:string) = JsonConvert.DeserializeObject<Person>(str)
以下是选项转换器:
namespace Newtonsoft.Json.FSharp
open System
open System.Collections.Generic
open Microsoft.FSharp.Reflection
open Newtonsoft.Json
open Newtonsoft.Json.Converters
/// Converts F# Option values to JSON
type OptionConverter() =
inherit JsonConverter()
override x.CanConvert(t) =
t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<option<_>>
override x.WriteJson(writer, value, serializer) =
let value =
if value = null then null
else
let _,fields = FSharpValue.GetUnionFields(value, value.GetType())
fields.[0]
serializer.Serialize(writer, value)
override x.ReadJson(reader, t, existingValue, serializer) =
let innerType = t.GetGenericArguments().[0]
let innerType =
if innerType.IsValueType then typedefof<Nullable<_>>.MakeGenericType([|innerType|])
else innerType
let value = serializer.Deserialize(reader, innerType)
let cases = FSharpType.GetUnionCases(t)
if value = null then FSharpValue.MakeUnion(cases.[0], [||])
else FSharpValue.MakeUnion(cases.[1], [|value|])
我想将选项字段序列化为值(如果它不是None),如果它是None,则序列化为null。反之亦然,null-
序列化工作正常:
[
{
"Name": "Scorpion King",
"Age": 30,
"Children": 3
},
{
"Name": "Popeye",
"Age": 40,
"Children": null
}
]
但是,当我发布到url时,Person参数被序列化为null,并且不会调用ReadJson方法。我使用Postman(chrome应用程序)通过选择主体来发布-
在WebApiConfig.cs我有:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new Newtonsoft.Json.FSharp.OptionConverter());
然而,如果我只是有了这个,并从Children字段中删除了JsonConverter属性,它似乎没有任何效果。
这是发送到服务器的内容:
POST /api/v1/Test HTTP/1.1
Host: localhost:8249
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: b831e048-2317-2580-c62f-a00312e9103b
Name=Blob&Age=103&Children=2
所以,我不知道出了什么问题,为什么反序列化器将对象转换为null。如果我删除选项字段它工作正常。此外,如果我从有效载荷中删除儿童字段,它也可以正常工作。我不明白为什么不调用OptionCoverter的ReadJson方法。
有什么想法吗?
谢啦
更新:在评论中,我没有发布应用程序/json有效载荷,这是正确的。我做到了,序列化仍然不能正常工作。
更新2:经过更多测试后,这项工作:
public IHttpActionResult Post(/*[FromBody]Test.Person value */)
{
HttpContent requestContent = Request.Content;
string jsonContent = requestContent.ReadAsStringAsync().Result;
var value = Test.deserializePerson(jsonContent);
return Json(Test.doSomethingCrazy(value));
}
以下是我用于测试请求的Linqpad代码(我没有使用postman):
var baseAddress = "http://localhost:49286/api/v1/Test";
var http = (HttpWebRequest) WebRequest.Create(new Uri(baseAddress));
http.Accept = "application/json";
http.ContentType = "application/json";
http.Method = "POST";
string parsedContent = "{\"Name\":\"Blob\",\"Age\":100,\"Children\":2}";
ASCIIEncoding encoding = new ASCIIEncoding();
Byte[] bytes = encoding.GetBytes(parsedContent);
Stream newStream = http.GetRequestStream();
newStream.Write(bytes, 0, bytes.Length);
newStream.Close();
var response = http.GetResponse();
var stream = response.GetResponseStream();
var sr = new StreamReader(stream);
var content = sr.ReadToEnd();
content.Dump();
更新3:
这很好用-例如,我使用了C类:
public IHttpActionResult Post(/*[FromBody]Test.Person*/ Person2 value)
{
// HttpContent requestContent = Request.Content;
// string jsonContent = requestContent.ReadAsStringAsync().Result;
//
// var value = Test.deserializePerson(jsonContent);
value.Children = value.Children.HasValue ? value.Children.Value + 1 : 1;
return Json(value);//Json(Test.doSomethingCrazy(value));
}
public class Person2
{
public string Name { get; set; }
public int Age { get; set; }
public int? Children { get; set; }
}
如果你愿意升级框架版本,你可以使用新的系统。文本Json
API应该是好的和快速的,以及FSharp。SystemTextJson
软件包为您提供了一个自定义转换器,支持记录、区分工会等。
根据这篇文章,我找到了一个解决方案:https://stackoverflow.com/a/26036775/832783.
我在WebApiConfig注册方法中添加了以下行:
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
我还得调查这里发生了什么。
更新1:
事实证明,在ApiController方法中调用Json(...)创建了一个新的JsonSeriazerSet对象,而不是使用global.asax.中的默认设置。这意味着,当使用json()结果。
根据这里描述的内容,我应该使用JSR-310表示而不是数字表示来序列化对象。但是,我得到了数字表示。可能有什么问题? 我是这样配置我正在使用的映射器的: 这是我得到的反序列化示例 对于以下事例类:
我对SOAP响应有问题,希望有人能帮助我。 当SOAP响应被SOAP::Lite模块反序列化时,我认为出了问题。在SOAP-响应中有一些“正常”节点和被引用的节点。但它不正确的反序列化。 SOAP响应: 结果哈希的转储: Perl代码:
问题内容: 我希望我的笔记应用程序能为您提供一些帮助。假设我的记事清单上有3个记事。我想删除列表顶部的注释。无论我尝试删除哪一个,始终会首先删除列表最底部的注释。我检查了React控制台,并且处于应用程序组件状态的notes数组表示已正确删除。但实际上,事实并非如此。如何获得它以便删除我选择的确切音符? 问题答案: 用作时发生此错误。React使用该属性来跟踪列表中的元素。当您从数组中间删除元素时
上次我在考虑如何在我们的应用程序中正确使用记录器。例如,我有一个控制器,它返回一个用户流,但在日志中,我看到“Fetch users”日志是由另一个线程记录的,而不是处理管道上的线程,但这是一个好的方法吗? 在这种情况下,使用了两个线程,从我的角度来看,这不是一个好的选择,但我找不到在反应应用程序中使用记录器的好做法。我认为下面的方法更好,因为分配内存是来自处理线程,而不是来自spring web
由于没有,可以使用什么本机实现来处理这个问题? 我注意到,我可以用它格式化数据到JSON,但是我如何反序列化? 或者我在?
我试图序列化和反序列化一个对象在java中使用Prop3 下面是我在java中的序列化和反序列化的样子 我的输出如下 当我试图使用ByteString和utf-8路由进行序列化和反序列化时,我哪里出错了? 谢谢!