通用 WCF JSON 反序列化

发布于 2024-08-22 08:59:49 字数 1076 浏览 12 评论 0原文

我对 WCF 有点陌生,将尝试清楚地描述我正在尝试做的事情。

我有一个使用 JSON 请求的 WCF Web 服务。我大部分情况下发送/接收 JSON 都做得很好。例如,以下代码运行良好且符合预期。

JSON 已发送:

{ "guy": {"FirstName":"Dave"} }

WCF:

    [DataContract]
    public class SomeGuy
    {
        [DataMember]
        public string FirstName { get; set; }
    }

    [OperationContract]
    [WebInvoke(Method = "POST",
               BodyStyle = WebMessageBodyStyle.WrappedRequest,
               RequestFormat = WebMessageFormat.Json,
               ResponseFormat = WebMessageFormat.Json)]
    public string Register(SomeGuy guy)
    {
        return guy.FirstName;
    }

这会按预期返回带有“Dave”的 JSON 对象。问题是我不能总是保证我收到的 JSON 与我的 DataContract 中的成员完全匹配。例如,JSON:

{ "guy": {"firstname":"Dave"} }

将无法正确序列化,因为大小写不匹配。 Guy.FirstName 将为空。这种行为是有道理的,但我真的不知道如何解决这个问题。我是否必须在客户端强制使用字段名称,或者是否有办法在服务器端进行协调?

一个可能相关的问题:我可以接受通用 JSON 对象并将其序列化为 StringDictionary 或某种简单的键值结构吗?那么无论 JSON 中发送的字段名称是什么,我都可以访问已发送给我的名称和值吗?现在,我可以读取收到的数据的唯一方法是它是否与预定义的 DataContract 完全匹配。

I am a bit new to WCF and will try to clearly describe what I am trying to do.

I have a WCF webservice that uses JSON requests. I am doing fine sending/receiving JSON for the most part. For example, the following code works well and as expected.

JSON sent:

{ "guy": {"FirstName":"Dave"} }

WCF:

    [DataContract]
    public class SomeGuy
    {
        [DataMember]
        public string FirstName { get; set; }
    }

    [OperationContract]
    [WebInvoke(Method = "POST",
               BodyStyle = WebMessageBodyStyle.WrappedRequest,
               RequestFormat = WebMessageFormat.Json,
               ResponseFormat = WebMessageFormat.Json)]
    public string Register(SomeGuy guy)
    {
        return guy.FirstName;
    }

This returns a JSON object with "Dave" as expected. The problem is that I cannot always guarantee that the JSON I recieve will exactly match the members in my DataContract. For example, the JSON:

{ "guy": {"firstname":"Dave"} }

will not serialize correctly because the case does not match. guy.FirstName will be null. This behavior makes sense, but I don't really know how to get around this. Do I have to force the field names on the client or is there a way I can reconcile on the server side?

A possibly related question: can I accept and serialize a generic JSON object into a StringDictionary or some kind of simple key value structure? So no matter what the field names are sent in the JSON, I can access names and values that have been sent to me? Right now, the only way I can read the data I'm receiving is if it exactly matches a predefined DataContract.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

凡间太子 2024-08-29 08:59:49

这是将 json 读入字典的另一种方法:

[DataContract]
public class Contract
    {
    [DataMember]
    public JsonDictionary Registration { get; set; }
    }

[Serializable]
public class JsonDictionary : ISerializable
    {
    private Dictionary<string, object> m_entries;

    public JsonDictionary()
        {
        m_entries = new Dictionary<string, object>();
        }

    public IEnumerable<KeyValuePair<string, object>> Entries
        {
        get { return m_entries; }
        }

    protected JsonDictionary(SerializationInfo info, StreamingContext context)
        {
        m_entries = new Dictionary<string, object>();
        foreach (var entry in info)
            {
            m_entries.Add(entry.Name, entry.Value);
            }
        }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        foreach (var entry in m_entries)
            {
            info.AddValue(entry.Key, entry.Value);
            }
        }
    }

Here's an alternative way to read json into dictionary:

[DataContract]
public class Contract
    {
    [DataMember]
    public JsonDictionary Registration { get; set; }
    }

[Serializable]
public class JsonDictionary : ISerializable
    {
    private Dictionary<string, object> m_entries;

    public JsonDictionary()
        {
        m_entries = new Dictionary<string, object>();
        }

    public IEnumerable<KeyValuePair<string, object>> Entries
        {
        get { return m_entries; }
        }

    protected JsonDictionary(SerializationInfo info, StreamingContext context)
        {
        m_entries = new Dictionary<string, object>();
        foreach (var entry in info)
            {
            m_entries.Add(entry.Name, entry.Value);
            }
        }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
        foreach (var entry in m_entries)
            {
            info.AddValue(entry.Key, entry.Value);
            }
        }
    }
幽梦紫曦~ 2024-08-29 08:59:49

顾名思义,数据契约是一组规则。如果您想可靠地将消息映射到操作,则需要遵循这些规则。

为什么不能保证外壳正确?如果您只想使用 JavaScript 中的小写标识符,则可以使用 MessageParameter 属性 - 但您仍然必须选择一个特定名称。

理论上,您可以接受原始 JSON 并手动对其进行反序列化(只需采用字符串参数并使用任何 JSON 库进行反序列化),但这实际上不符合 WCF 的精神。

我认为您真正需要解决的问题不是数据协定区分大小写,而是 JSON 没有在客户端正确组合在一起。


如果您想在操作中接受原始 JSON 字符串,请将 BodyStyle 更改为 WebMessageBodyStyle.Bare,并更改方法签名以接受单个字符串参数,这将使用客户端发送的任何 JSON 字符串进行填充。

请注意,您得到的只是一个字符串,您必须自己完成所有解析、属性映射、验证和错误处理。这不是我会选择采取的路线,但如果您愿意承担相关的努力和风险,这是一种潜在的选择。

As the name implies, a data contract is a set of rules. If you want to reliably map messages to operations, then those rules need to be followed.

Why can't you guarantee that the casing will be correct? If you just want to use lowercase identifiers from JavaScript instead, you can use the MessageParameter attribute for that - but you still have to choose a specific name.

In theory you could accept raw JSON and deserialize it manually (just take a string parameter and use any JSON library for deserialization), but that is really not in the spirit of WCF.

I think what you really need to fix is not the fact that the data contract is case sensitive, but the fact that the JSON isn't being put together correctly at the client side.


If you want to accept the raw JSON string in your operation, then change the BodyStyle to WebMessageBodyStyle.Bare, and change your method signature to accept a single string parameter, which will be populated with whatever JSON string was sent by the client.

Note that all you get is a single string out of this, you have to do all the parsing and property mapping and validation and error handling yourself. It's not the route I would choose to take, but if you're willing to take on the effort and risk involved, it's one potential option.

安静 2024-08-29 08:59:49

您可以尝试添加另一个 DataMember attribute具有小写名称,但我假设您需要一种不区分大小写的方法来协调成员名称,在这种情况下,使用额外的 DataMember 属性变得不合理。

可以提供IDataContractSurrogate 实现,但这将涉及您大量的额外工作,以及大量的反射(没有办法以可以在编译时验证的静态方式来执行此操作)。

就我个人而言,我已经放弃使用 DataContractSerializer 当涉及到 JSON 时。相反,我使用 Json.NET 来满足我的所有 JSON 需求。我可以看到您可以在哪里通过 IDataContractSurrogate 将 Json.NET 插入到 DataContractSerializer 中,但它仍然有点粗糙。

考虑到使用 DataContractSerializer 的困难,Json.NET 是一个简单的选择。另外,添加到 JSON 的 DataContractSerializer 不处理 DateTimeOffset 值正确,这对我来说是理所当然的,特别是因为我在 ASP.NET MVC 环境中工作(这允许我塑造任何我想要的结果)。

如果您使用 JSON 作为编码来公开 RESTful 服务,这确实是更好的选择,您可以将其与 WCF 混合搭配,以公开您需要的所有传输和消息协议上的所有端点。

You can try and add another DataMember attribute with the lowercase name, but I assume you want a case-insensitive approach to reconciling member names, in which case, using extra DataMember attributes becomes unreasonable.

You could provide a IDataContractSurrogate implementation, but that is going to involve a lot of extra work on your part, as well as a lot of reflection (there's no way to do this in a static way which can be verified at compile-time).

Personally, I've given up on using the DataContractSerializer class when it comes to JSON. Rather, I use Json.NET for all of my JSON needs. I can see where you might be able to plug in Json.NET into the DataContractSerializer through a IDataContractSurrogate, but it's still going to be a little rough.

Json.NET was an easy choice given the difficulty of using the DataContractSerializer. Also, add to the fact that the DataContractSerializer for JSON doesn't handle DateTimeOffset values correctly, and it was a no-brainer for me, especially since I was working in an ASP.NET MVC environment (which allows me to shape the result any way I want).

This is really the better choice if you are exposing RESTful services using JSON as the encoding, you can mix and match that with WCF to expose all the endpoints over all the transport and message protocols you need.

帥小哥 2024-08-29 08:59:49

为了实现我的目标,即拥有一个可以接受完全任意的“键”:“值”对列表作为原始 JSON 字符串,然后决定如何处理它们,而无需事先知道“键”名称,我结合了卡斯帕和亚伦的建议。

1、访问原始 JSON 字符串, 这个 MSDN 博客非常有帮助。

我无法简单地将单个方法参数更改为 String 并将 BodyStyle 更改为 WebMessageBodyStyle.Bare 没有问题。将 BodyStyle 设置为 Bare 时,请确保端点 behaviorConfiguration 设置为 而不是

第二个注意事项是,正如 casperOne 提到的,该方法只能有 1 个范围。不过,此参数必须是 Stream 才能访问原始文本(请参阅上面的 MSDN 博客)。

获得原始 JSON 字符串后,只需将其反序列化为 StringDictionary 即可。我为此选择了 JSON.Net,它运行得非常好。这是一个简单的例子来说明。

[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare,
    ResponseFormat = WebMessageFormat.Json)]
public string Register(Stream rawJSON)
{ 
    // Convert our raw JSON string into a key, value
    StreamReader sr = new StreamReader(rawJSON);
    Dictionary<string, string> registration =     
        JsonConvert.DeserializeObject<Dictionary<string, string>>(
            sr.ReadToEnd());

    // Loop through the fields we've been sent.
    foreach (KeyValuePair<string, string> pair in registration)
    {
        switch (pair.Key.ToLower())
        {
            case "firstname":
                return pair.Value;
                break;
        }

    }
}

这允许我通过 JSON 接受任意字段列表,并且字段名称不区分大小写。我知道这不是实现数据完整性或 WCF 服务理想结构的最严格方法,但据我所知,这是实现我想要的目标的最简单方法。

In order to achieve my goal of having a service that can accept a completely arbitrary list of "key": "value" pairs as a raw JSON string and then decide what do do with them without having to know the "key" names beforehand, I combined casper and aaron's advice.

1st, to access the raw JSON string, this MSDN blog was very helpful.

I was unable to simply change the single method parameter to String and the BodyStyle to WebMessageBodyStyle.Bare without problems. When setting BodyStyle to Bare, make sure that the endpoint behaviorConfiguration is set to <webHttp/> and not <enableWebScript/>.

The second note is that, as casperOne mentioned, the method can only have 1 parameter. This parameter needs to be a Stream in order to access the raw text though (see MSDN blog above).

Once you have the raw JSON string, it's just a matter of deserializing it into a StringDictionary. I chose JSON.Net for this and it works wonderfully. Here's a bare bones example to illustrate.

[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare,
    ResponseFormat = WebMessageFormat.Json)]
public string Register(Stream rawJSON)
{ 
    // Convert our raw JSON string into a key, value
    StreamReader sr = new StreamReader(rawJSON);
    Dictionary<string, string> registration =     
        JsonConvert.DeserializeObject<Dictionary<string, string>>(
            sr.ReadToEnd());

    // Loop through the fields we've been sent.
    foreach (KeyValuePair<string, string> pair in registration)
    {
        switch (pair.Key.ToLower())
        {
            case "firstname":
                return pair.Value;
                break;
        }

    }
}

This allows me to accept an arbitrary list of fields via JSON and for the field names to be case insensitive. I know it's not the strictest approach to data integrity or to how WCF services would ideally be structured, but, as far as I can tell, it's the simplest way to get where I want to go.

蹲在坟头点根烟 2024-08-29 08:59:49

这实际上取决于您使用数据的目的。理论上,您可以通过使用字符串返回类型来保持这种通用性。然而,数据将表示一个对象,并且客户端控件应该知道如何枚举返回的 JSON 对象。您可以返回类型,这也可能有帮助。

然后,在您的客户端代码中,您可以拥有一个知道如何确定和读取不同类型的功能。

It really depends on what you are using the data for. In theory, you can keep this generic by having a string return type. However the data would represent an object and the client side control should know how to enumerate the JSON Object returned. You can return type which may also help.

Then in your client side code you can have a feature that knows how to determine and read different types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文