我在Python中有一个字典列表。此列表作为Web服务之间的json传递。这些Web服务基于传递的json创建独特的签名。创建签名的一部分是规范化数据有效负载并确保所有内容都处于正确的顺序,所以我这样做(在Python中) - 工作正常。
data = [{'a': '1', 'b': '2', 'c': 3}, {'d': 3}, {3: 1}, {'100': '200'}]
sorted(data)
> [{3: 1}, {'100': '200'}, {'d': 3}, {'a': '1', 'c': 3, 'b': '2'}]
现在,我需要在混合中添加一个C#应用程序,它需要能够创建与Python代码完全相同的签名。我没有发现以与Python的sorted
内置函数相同的方式对上述数据结构进行排序的秘诀。
我正在使用ServiceStack来解析json数据。
我希望它会像做这样的事情一样容易(在C#中):
var jsonPayload = "[{\"a\": \"1\", \"b\": \"2\", \"c\": 3}, {\"d\": 3}, {3: 1}, {\"100\": \"200\"}]";
var parsedJson = JsonArrayObjects.Parse(jsonPayload);
parsedJson.Sort();
但是,我从上面的C#代码中得到了这个例外:
`At least one object just implement IComparable`
我理解为什么我会收到此错误,但我不确定应该怎么做。我真的希望我不必滚动自己的排序逻辑。我正在处理的实际数据非常动态。这只是阻碍我前进的一个例子。
有没有人对如何在C#中使用sorted
python函数对这种类型的嵌套数据结构进行排序提出任何建议或建议?
谢谢!
答案 0 :(得分:0)
示例数据无效JSON,如图所示。整数三不能是键,它必须是一个字符串。将{3: 1}
更改为{"3": 1}
第二个问题是默认情况下C#词典不可订购。但是,您可以将它们子类化,以使它们可以订购。
用于排序词典的Python2.x算法是:
1)如果字典的大小不同,那么长度较短的字典就是较小的值。
2)如果大小相同,则扫描第一个字典以找到第一个字典中的最小键,该键不存在或在第二个字典中具有不匹配的值。不匹配的值确定哪个字典最大。
以下是 Objects / dictobject.c 的Python2.7源代码的相关摘录:
/* Subroutine which returns the smallest key in a for which b's value
is different or absent. The value is returned too, through the
pval argument. Both are NULL if no key in a is found for which b's status
differs. The refcounts on (and only on) non-NULL *pval and function return
values must be decremented by the caller (characterize() increments them
to ensure that mutating comparison and PyDict_GetItem calls can't delete
them before the caller is done looking at them). */
static PyObject *
characterize(PyDictObject *a, PyDictObject *b, PyObject **pval)
{
PyObject *akey = NULL; /* smallest key in a s.t. a[akey] != b[akey] */
PyObject *aval = NULL; /* a[akey] */
Py_ssize_t i;
int cmp;
for (i = 0; i <= a->ma_mask; i++) {
PyObject *thiskey, *thisaval, *thisbval;
if (a->ma_table[i].me_value == NULL)
continue;
thiskey = a->ma_table[i].me_key;
Py_INCREF(thiskey); /* keep alive across compares */
if (akey != NULL) {
cmp = PyObject_RichCompareBool(akey, thiskey, Py_LT);
if (cmp < 0) {
Py_DECREF(thiskey);
goto Fail;
}
if (cmp > 0 ||
i > a->ma_mask ||
a->ma_table[i].me_value == NULL)
{
/* Not the *smallest* a key; or maybe it is
* but the compare shrunk the dict so we can't
* find its associated value anymore; or
* maybe it is but the compare deleted the
* a[thiskey] entry.
*/
Py_DECREF(thiskey);
continue;
}
}
/* Compare a[thiskey] to b[thiskey]; cmp <- true iff equal. */
thisaval = a->ma_table[i].me_value;
assert(thisaval);
Py_INCREF(thisaval); /* keep alive */
thisbval = PyDict_GetItem((PyObject *)b, thiskey);
if (thisbval == NULL)
cmp = 0;
else {
/* both dicts have thiskey: same values? */
cmp = PyObject_RichCompareBool(
thisaval, thisbval, Py_EQ);
if (cmp < 0) {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
goto Fail;
}
}
if (cmp == 0) {
/* New winner. */
Py_XDECREF(akey);
Py_XDECREF(aval);
akey = thiskey;
aval = thisaval;
}
else {
Py_DECREF(thiskey);
Py_DECREF(thisaval);
}
}
*pval = aval;
return akey;
Fail:
Py_XDECREF(akey);
Py_XDECREF(aval);
*pval = NULL;
return NULL;
}
static int
dict_compare(PyDictObject *a, PyDictObject *b)
{
PyObject *adiff, *bdiff, *aval, *bval;
int res;
/* Compare lengths first */
if (a->ma_used < b->ma_used)
return -1; /* a is shorter */
else if (a->ma_used > b->ma_used)
return 1; /* b is shorter */
/* Same length -- check all keys */
bdiff = bval = NULL;
adiff = characterize(a, b, &aval);
if (adiff == NULL) {
assert(!aval);
/* Either an error, or a is a subset with the same length so
* must be equal.
*/
res = PyErr_Occurred() ? -1 : 0;
goto Finished;
}
bdiff = characterize(b, a, &bval);
if (bdiff == NULL && PyErr_Occurred()) {
assert(!bval);
res = -1;
goto Finished;
}
res = 0;
if (bdiff) {
/* bdiff == NULL "should be" impossible now, but perhaps
* the last comparison done by the characterize() on a had
* the side effect of making the dicts equal!
*/
res = PyObject_Compare(adiff, bdiff);
}
if (res == 0 && bval != NULL)
res = PyObject_Compare(aval, bval);
Finished:
Py_XDECREF(adiff);
Py_XDECREF(bdiff);
Py_XDECREF(aval);
Py_XDECREF(bval);
return res;
}
答案 1 :(得分:0)
你可以解决它:从C#调用Python排序函数,这样你就会有完全相同的行为。
您可以使用IronPython:
Python代码:
def Simple():
print "Hello from Python"
print "Call Dir(): "
print dir()
C#代码:
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class dynamic_demo
{
static void Main()
{
var ipy = Python.CreateRuntime();
dynamic test = ipy.UseFile("Test.py");
test.Simple();
}
}
完整示例here。
http://blogs.msdn.com/b/charlie/archive/2009/10/25/hosting-ironpython-in-a-c-4-0-program.aspx
答案 2 :(得分:0)
另一种选择是在C#app中从命令行调用Python排序函数。您可以使用此方法:
private void run_cmd(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "my/full/path/to/python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using(Process process = Process.Start(start))
{
using(StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
有关此内容的详情,请访问:How do I run a Python script from C#?