malloc: *** 释放对象指针的错误在 JSON 反序列化期间未分配

malloc: *** error for object pointer being freed was not allocated during JSON deserializing

我正在尝试使用 Visual Studio 中的 C# 将 JSON 字符串反序列化为列表,用于 Xamarin.Mac 项目的 Mac。

我可以毫无问题地进行序列化:

using (StreamWriter file = File.CreateText(filename))
            {
                var serializer = new JsonSerializer()
                {
                    Formatting = Formatting.Indented,
                    TypeNameHandling = TypeNameHandling.Auto,
                    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                };
                await Task.Run(() => serializer.Serialize(file, contactroot));
            }

但是当我尝试使用此反序列化时出现上述错误:

var contacts = JsonConvert.DeserializeObject<ContactRoot>(json);

联系人根class:

public class ContactRoot
{
    public List<Contact> contacts { get; set; }
}

Contact class 继承了 NSObject class 以绑定到 Mac 应用程序中的控件。我也会包括它,但有超过 70 个具有多个属性的属性。这是 class 中的几个片段:

[Register("Contact")]
public class Contact : NSObject

...还有很多这样的属性:

public DateTime? updated_at { get; set; }

    [Export("updatedString"), Display(Name = "Updated Date"), Category("Misc"), Description("3"), DefaultValue(TypeOfValue.DateTime)]
    public string updatedString
    {
        get {
            if (this.updated_at.HasValue){
                return this.updated_at.Value.ToString();
            } else{
                return string.Empty;
            }
        }
        set
        {
            WillChangeValue("updatedString");
            try
            {
                if (!String.IsNullOrEmpty(value)) this.updated_at = Convert.ToDateTime(value);
            }
            catch (Exception)
            {
            }
            DidChangeValue("updatedString");
        }
    }

我正在使用 DateTime?因为 API 源需要这样做,并且由于 Xamarin 数据绑定似乎不处理 int 或 DateTime 的空值,所以我使用字符串 属性 进行转换。使用该应用程序时效果很好,但不确定反序列化时是否会导致错误。 我是 Mac 的开发新手,所以我可能错过了一些东西。

编辑

哇,感谢您的快速回复!在看到任何评论之前,我尝试了一个更简单的 class 但得到了相同的结果:

[Register("SimpleContact")]
public class SimpleContact : NSObject
{
    int _id = 0;
    string _first = String.Empty;
    string _last = String.Empty;

    [Export("id")]
    public int id {
        get { return _id; }
        set {
            WillChangeValue("id");
            _id = value;
            DidChangeValue("id");
        }
    }

    [Export("first")]
    public string first {
        get { return _first; }
        set {
            WillChangeValue("first");
            _first = value;
            DidChangeValue("first");
        }
    }

    [Export("last")]
    public string last
    {
        get { return _last; }
        set
        {
            WillChangeValue("last");
            _last = value;
            DidChangeValue("last");
        }
    }

    [Export("fullname")]
    public string fullname
    {
        get { return this.first + " " + this.last; }
    }

    public SimpleContact()
    {}

    public SimpleContact(int id, string first, string last)
    {
        this.id = id;
        this.first = first;
        this.last = last;
    }

    public override string ToString()
    {
        return this.fullname;
    }
}

接下来我将尝试 POCO 方法并 post 结果。非常感谢!

编辑 这是异常的实质内容。我认为其余的异常没有任何用处:

Crashed Thread:        0  tid_307  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

External Modification Warnings:
Debugger attached to process.

Application Specific Information:
abort() called
WealthBox Addon(6016,0x11d9ae5c0) malloc: *** error for object 0x7fff7f110880: pointer being freed was not allocated


Thread 0 Crashed:: tid_307  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib          0x00007fff7f08223e __pthread_kill + 10
1   libsystem_pthread.dylib         0x00007fff7f138c1c pthread_kill + 285
2   libsystem_c.dylib               0x00007fff7efeb268 __abort + 144
3   libsystem_c.dylib               0x00007fff7efeb1d8 abort + 142
4   libsystem_malloc.dylib          0x00007fff7f0fa6e2 malloc_vreport + 545
5   libsystem_malloc.dylib          0x00007fff7f0fa4a3 malloc_report + 152
6   libsystem_malloc.dylib          0x00007fff7f0f2aff malloc_set_zone_name + 47
7   ???                             0x00000001162ad420 0 + 4666872864
8   ???                             0x000000011616c6a3 0 + 4665558691
9   ???                             0x0000000116275739 0 + 4666644281
10  ???                             0x0000000116265443 0 + 4666577987
11  ???                             0x000000011625cbd3 0 + 4666543059
12  ???                             0x000000011618480b 0 + 4665657355
13  ???                             0x00000001162755bb 0 + 4666643899
14  ???                             0x0000000116265443 0 + 4666577987
15  ???                             0x000000011625cbd3 0 + 4666543059
16  ???                             0x000000011618480b 0 + 4665657355
17  ???                             0x000000011618885b 0 + 4665673819
18  ???                             0x0000000116185c7b 0 + 4665662587
19  ???                             0x000000011618486b 0 + 4665657451
20  ???                             0x000000011609f303 0 + 4664718083
21  ???                             0x000000011609cc03 0 + 4664708099
22  ???                             0x000000011609c84e 0 + 4664707150
23  ???                             0x0000000116095663 0 + 4664677987
24  ???                             0x0000000116087a33 0 + 4664621619
25  Browning-Financial.WealthBox-Addon  0x000000010edc5593 mono_jit_runtime_invoke + 1443 (mini-runtime.c:2806)
26  Browning-Financial.WealthBox-Addon  0x000000010eeaee4f mono_runtime_invoke_checked + 127 (object.c:2887)
27  Browning-Financial.WealthBox-Addon  0x000000010eeb36fc mono_runtime_invoke + 76 (object.c:2941)
28  Browning-Financial.WealthBox-Addon  0x000000010ecadaa2 xamarin_invoke_trampoline + 6018 (trampolines-invoke.m:456)
29  Browning-Financial.WealthBox-Addon  0x000000010ecaec7d xamarin_arch_trampoline + 189 (trampolines-x86_64.m:547)
30  Browning-Financial.WealthBox-Addon  0x000000010ecb00b1 xamarin_x86_64_common_trampoline + 110
31  com.apple.AppKit                0x00007fff4f3deb5a -[NSViewController _sendViewDidLoad] + 97
32  com.apple.AppKit                0x00007fff4f3dbeb5 _noteLoadCompletionForObject + 645
33  com.apple.AppKit                0x00007fff4f24d99f -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] + 2086
34  com.apple.AppKit                0x00007fff4f3e3114 -[NSNib _instantiateNibWithExternalNameTable:options:] + 679
35  com.apple.AppKit                0x00007fff4f3e2d70 -[NSNib _instantiateWithOwner:options:topLevelObjects:] + 136
36  com.apple.AppKit                0x00007fff4f3e1fdc -[NSViewController loadView] + 343
37  com.apple.AppKit                0x00007fff4f3ddb60 -[NSViewController _loadViewIfRequired] + 75
38  com.apple.AppKit                0x00007fff4f3ddacb -[NSViewController view] + 30
39  com.apple.AppKit                0x00007fff4f4d01da -[NSWindow _contentViewControllerChanged] + 109
40  com.apple.Foundation            0x00007fff540c7450 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 331
41  com.apple.AppKit                0x00007fff4f3da987 -[NSWindow setValue:forKey:] + 111
42  com.apple.AppKit                0x00007fff4f32ce0c -[NSIBUserDefinedRuntimeAttributesConnector establishConnection] + 637
43  com.apple.AppKit                0x00007fff4f24d739 -[NSIBObjectData nibInstantiateWithOwner:options:topLevelObjects:] + 1472
44  com.apple.AppKit                0x00007fff4f3e3114 -[NSNib _instantiateNibWithExternalNameTable:options:] + 679
45  com.apple.AppKit                0x00007fff4f3e2d70 -[NSNib _instantiateWithOwner:options:topLevelObjects:] + 136
46  com.apple.AppKit                0x00007fff4fada64c -[NSStoryboard instantiateControllerWithIdentifier:] + 236
47  com.apple.AppKit                0x00007fff4f23e855 NSApplicationMain + 702
48  ???                             0x00000001151c56b7 0 + 4649146039
49  ???                             0x00000001151c53ab 0 + 4649145259
50  Browning-Financial.WealthBox-Addon  0x000000010edc5593 mono_jit_runtime_invoke + 1443 (mini-runtime.c:2806)
51  Browning-Financial.WealthBox-Addon  0x000000010eeaee4f mono_runtime_invoke_checked + 127 (object.c:2887)
52  Browning-Financial.WealthBox-Addon  0x000000010eeb5c2e mono_runtime_exec_main_checked + 110 (object.c:4782)
53  Browning-Financial.WealthBox-Addon  0x000000010ed1087f mono_jit_exec + 287 (driver.g.c:1210)
54  Browning-Financial.WealthBox-Addon  0x000000010ed1384a mono_main + 11114 (driver.g.c:2418)
55  Browning-Financial.WealthBox-Addon  0x000000010ecb0a8e xamarin_main + 1182 (launcher.m:661)
56  Browning-Financial.WealthBox-Addon  0x000000010ecb1a04 main + 36 (launcher.m:679)
57  libdyld.dylib                   0x00007fff7ef42ed9 start + 1

Thread 1:
0   libsystem_pthread.dylib         0x00007fff7f1353f8 start_wqthread + 0
1   ???                             0x0000000054485244 0 + 1414025796

Thread 2:: SGen worker
0   libsystem_kernel.dylib          0x00007fff7f07f7de __psynch_cvwait + 10
1   libsystem_pthread.dylib         0x00007fff7f139593 _pthread_cond_wait + 724
2   Browning-Financial.WealthBox-Addon  0x000000010ef7416e thread_func + 558 (mono-os-mutex.h:173)
3   libsystem_pthread.dylib         0x00007fff7f136305 _pthread_body + 126
4   libsystem_pthread.dylib         0x00007fff7f13926f _pthread_start + 70
5   libsystem_pthread.dylib         0x00007fff7f135415 thread_start + 13

Thread 3:: Finalizer
0   libsystem_kernel.dylib          0x00007fff7f07c1b6 semaphore_wait_trap + 10
1   Browning-Financial.WealthBox-Addon  0x000000010ee4c7a5 finalizer_thread + 293 (mono-os-semaphore.h:90)
2   Browning-Financial.WealthBox-Addon  0x000000010ef01b50 start_wrapper + 704 (threads.c:1004)
3   libsystem_pthread.dylib         0x00007fff7f136305 _pthread_body + 126
4   libsystem_pthread.dylib         0x00007fff7f13926f _pthread_start + 70
5   libsystem_pthread.dylib         0x00007fff7f135415 thread_start + 13

Thread 4:: Debugger agent
0   libsystem_kernel.dylib          0x00007fff7f07f3e6 __recvfrom + 10
1   Browning-Financial.WealthBox-Addon  0x000000010ed0515e socket_transport_recv + 78 (debugger-agent.c:1153)
2   Browning-Financial.WealthBox-Addon  0x000000010ecf5a7c debugger_thread + 28588 (debugger-agent.c:1558)
3   Browning-Financial.WealthBox-Addon  0x000000010ef01b50 start_wrapper + 704 (threads.c:1004)
4   libsystem_pthread.dylib         0x00007fff7f136305 _pthread_body + 126
5   libsystem_pthread.dylib         0x00007fff7f13926f _pthread_start + 70
6   libsystem_pthread.dylib         0x00007fff7f135415 thread_start + 13

Thread 5:
0   libsystem_pthread.dylib         0x00007fff7f1353f8 start_wqthread + 0
1   ???                             0x0000000054485244 0 + 1414025796

Thread 0 crashed with X86 Thread State (64-bit):
  rax: 0x0000000000000000  rbx: 0x000000011d9ae5c0  rcx: 0x00007ffee0f81a18  rdx: 0x0000000000000000
  rdi: 0x0000000000000307  rsi: 0x0000000000000006  rbp: 0x00007ffee0f81a50  rsp: 0x00007ffee0f81a18
   r8: 0x0000000000000000   r9: 0x0000000000989680  r10: 0x0000000000000000  r11: 0x0000000000000206
  r12: 0x0000000000000307  r13: 0x000000011597b000  r14: 0x0000000000000006  r15: 0x000000000000002d
  rip: 0x00007fff7f08223e  rfl: 0x0000000000000206  cr2: 0x00007fffb1d2c188

Logical CPU:     0
Error Code:      0x02000148
Trap Number:     133

当 serializing/deserializing 数据是 POD(普通旧数据,即仅包含 get/set 没有业务逻辑的字段属性时,最好坚持使用普通 DTO(数据传输对象)关联)。

从这个意义上说,您的 ContactRoot 类型是 POD,但是 Contact 类型不是 POD(由于 NSObject 的继承)。对于这种情况,我将为 Contact 引入一个单独的 DTO 类型,它将遵循 POD 标准。您可以为联系人列表创建替代 属性,并尝试使用 JsonIgnore 和 JsonProperty 属性来模仿所需的行为。

快速摘要:

public class ContactDto
{
    /* List relevant properties here */
}

public class ContactRoot
{
    [JsonIgnore] // Do not serialize this
    public List<Contact> Contacts { get; set; }

    [JsonProperty(Propertyname = nameof(Contacts)]
    public List<ContactDto> SerializableContacts { 
        get => Contacts?.Select(x => /* data extraction */).ToList();
        set => Contacts = value?.Select(x => /* data extraction */)?.ToList();
    }
}

补充:对于 CLR 语言,POD 似乎被称为 POCO

好的,感谢 Evengy Nazarov 为我指明了正确的方向,我有了一个可行的解决方案。

首先,这是我简化后的 POCO 版本 class:

public class ContactDto
{
    private string _fullname = String.Empty;

    public int id { get; set; }

    public string first { get; set; }

    public string last { get; set; }

    [JsonIgnore]
    public string fullname {
        get { return this.first + " " + this.last; }
        set { _fullname = value; }
    }
}

注意没有 NSObject 继承或 属性 修饰(除了 [JsonIgnore].

将 ContactRoot 更改为:

public class ContactRoot
{
    [JsonIgnore] // Do not serialize this
    public List<SimpleContact> SimpleContacts { get; set; }

    [JsonProperty(PropertyName = nameof(Contacts))]
    public List<ContactDto> SerializableContacts { get; set; }
}

然后我创建了转换方法,使用 System.Reflection:

在 DTO 和 NSObject 版本之间进行转换
public ContactDto ConvertToDto(SimpleContact simpleContact)
    {
        var contactdto = new ContactDto();
        foreach (PropertyInfo prop in simpleContact.GetType().GetProperties().Where(t => t.DeclaringType == typeof(SimpleContact)))
        {
            var value = prop.GetValue(simpleContact);
            if (value != null)
            {
                var prop2 = contactdto.GetType().GetProperty(prop.Name);
                prop2.SetValue(contactdto, value);
            }
        }
        return contactdto;
    }
public SimpleContact ConvertToContact(ContactDto contactDto)
    {
        var simplecontact = new SimpleContact();
        foreach (PropertyInfo prop in contactDto.GetType().GetProperties())
        {
            var value = prop.GetValue(contactDto);
            if (value != null)
            {
                var prop2 = simplecontact.GetType().GetProperty(prop.Name);
                prop2.SetValue(simplecontact, value);
            }
        }
        return simplecontact;
    }

然后是我的新序列化方法:

var contactroot = new ContactRoot
            {
                SimpleContacts = DataSource.SimpleContacts,
                SerializableContacts = new List<ContactDto>()
            };

            foreach (var simplecontact in contactroot.SimpleContacts)
            {
                contactroot.SerializableContacts.Add(ConvertToDto(simplecontact));
            }

            var desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            var filename = Path.Combine(desktop, "WBAddon.json");
            if (File.Exists(filename))
            {
                File.Delete(filename);
            }

            using (StreamWriter file = File.CreateText(filename))
            {
                var serializer = new JsonSerializer()
                {
                    Formatting = Formatting.Indented,
                    TypeNameHandling = TypeNameHandling.Auto,
                    TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                };
                await Task.Run(() => serializer.Serialize(file, contactroot));
            }

反序列化:

var jsettings = new JsonSerializerSettings
            {
                Formatting = Formatting.Indented,
                TypeNameHandling = TypeNameHandling.Auto,
                TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            };
            string json = string.Empty;
            using (StreamReader file = new StreamReader(filename))
            {
                json = file.ReadToEnd();

            }

            var contactroot = JsonConvert.DeserializeObject<ContactRoot>(json);
            DataSource.SimpleContacts = new List<SimpleContact>();
            foreach (var contactdto in contactroot.SerializableContacts)
            {
                DataSource.SimpleContacts.Add(ConvertToContact(contactdto));
            }

如我所愿!现在我只需要创建一个包含所有 70 多个属性的完整联系人 class 的 dto 版本。可能有更好的方法,但这可以完成工作。