Я столкнулся с этой проблемой, когда попытался сохранить модель Peewee в PostgreSQL JSONField
.
После долгой работы, вот общее решение.
Ключ к моему решение проходит через исходный код Python и понимает, что документация по коду (описанная здесь здесь ) уже объясняет, как расширить существующий json.dumps
для поддержки других типов данных.
Предположим, что вы иметь модель, которая содержит некоторые поля, которые не могут быть сериализованы для JSON, и модель, которая содержит поле JSON, первоначально выглядит следующим образом:
class SomeClass(Model):
json_field = JSONField()
Просто определите пользовательский JSONEncoder
, как это:
class CustomJsonEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
return < whatever value you want >
return json.JSONEncoder.default(self, obj)
@staticmethod
def json_dumper(obj):
return json.dumps(obj, cls=CustomJsonEncoder)
А затем просто используйте его в своем JSONField
, как показано ниже:
class SomeClass(Model):
json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)
Ключом является метод default(self, obj)
выше. Для каждой жалобы ... is not JSON serializable
, которую вы получаете от Python, просто добавьте код для обработки типа unserializable-to-JSON (например, Enum
или datetime
)
Например, вот как я поддерживаю класс, наследующий от Enum
:
class TransactionType(Enum):
CURRENT = 1
STACKED = 2
def default(self, obj):
if isinstance(obj, TransactionType):
return obj.value
return json.JSONEncoder.default(self, obj)
Наконец, с помощью кода, реализованного, как описано выше, вы можете просто преобразовать любые модели Peewee в объект JSON-seriazable, как показано ниже:
peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)
Хотя приведенный выше код был (несколько) специфичен для Peewee, но я думаю:
json.dumps
, это решение также работает с Python (без ORM) вообще . Все вопросы, пожалуйста, публикуйте в разделе комментариев. Спасибо!
Класс System.Version не поддерживает версии с запятыми в нем, поэтому решения, представленного Дарреном Коппом , недостаточно.
Вот версия, которая настолько проста, насколько это возможно (но не проще).
Он использует System.Version , но достигает совместимости с номерами версий, такими как «1, 2, 3, 4», выполняя поиск-замену перед сравнением.
/// <summary>
/// Compare versions of form "1,2,3,4" or "1.2.3.4". Throws FormatException
/// in case of invalid version.
/// </summary>
/// <param name="strA">the first version</param>
/// <param name="strB">the second version</param>
/// <returns>less than zero if strA is less than strB, equal to zero if
/// strA equals strB, and greater than zero if strA is greater than strB</returns>
public static int CompareVersions(String strA, String strB)
{
Version vA = new Version(strA.Replace(",", "."));
Version vB = new Version(strB.Replace(",", "."));
return vA.CompareTo(vB);
}
Код был протестирован с:
static void Main(string[] args)
{
Test("1.0.0.0", "1.0.0.1", -1);
Test("1.0.0.1", "1.0.0.0", 1);
Test("1.0.0.0", "1.0.0.0", 0);
Test("1, 0.0.0", "1.0.0.0", 0);
Test("9, 5, 1, 44", "3.4.5.6", 1);
Test("1, 5, 1, 44", "3.4.5.6", -1);
Test("6,5,4,3", "6.5.4.3", 0);
try
{
CompareVersions("2, 3, 4 - 4", "1,2,3,4");
Console.WriteLine("Exception should have been thrown");
}
catch (FormatException e)
{
Console.WriteLine("Got exception as expected.");
}
Console.ReadLine();
}
private static void Test(string lhs, string rhs, int expected)
{
int result = CompareVersions(lhs, rhs);
Console.WriteLine("Test(\"" + lhs + "\", \"" + rhs + "\", " + expected +
(result.Equals(expected) ? " succeeded." : " failed."));
}
Используйте класс Версии .
Version a = new Version("1.0.0.0");
Version b = new Version("2.0.0.0");
Console.WriteLine(string.Format("Newer: {0}", (a > b) ? "a" : "b"));
// prints b
Ну, так как у вас есть только массив из четырех элементов, вы можете просто развернуть рекурсию, чтобы сэкономить время. Передача массивов в качестве аргументов поглотит память и оставит беспорядок для GC, который будет убран позже.
Если можно предположить, что каждое место в строке версии только будет одним числом (или по крайней мере последние 3, можно просто удалить запятые или периоды и выдержать сравнение..., который был бы намного быстрее... не как устойчивый, но Вам не всегда нужно это.
public static int CompareVersions(string strA, string strB)
{
char[] splitTokens = new char[] {'.', ','};
string[] strAsplit = strA.Split(splitTokens, StringSplitOptions.RemoveEmptyEntries);
string[] strBsplit = strB.Split(splitTokens, StringSplitOptions.RemoveEmptyEntries);
int versionA = 0;
int versionB = 0;
string vA = string.Empty;
string vB = string.Empty;
for (int i = 0; i < 4; i++)
{
vA += strAsplit[i];
vB += strBsplit[i];
versionA[i] = Convert.ToInt32(strAsplit[i]);
versionB[i] = Convert.ToInt32(strBsplit[i]);
}
versionA = Convert.ToInt32(vA);
versionB = Convert.ToInt32(vB);
if(vA > vB)
return 1;
else if(vA < vB)
return -1;
else
return 0; //they are equal
}
И да, я также принимаю 4 места версии здесь...