Swift iOS开发:Realm与ObjectMapper字符串数组映射问题
Hey there! I see the issue you're running into—your JSON's authors field is an array of raw strings, but your current setup expects JSON objects to map to Author instances. Let's get this sorted out with a couple of straightforward solutions.
The Root Problem
Your existing ListTransform<Author> works when the JSON array contains objects (like [{"authorName": "Name FirstName"}]), but your JSON has just plain strings. We need a way to convert each string in the array into an Author object with its authorName set.
Solution 1: Custom Transform for String Array → List
This is the cleanest and most reusable approach. We'll create a custom TransformType that takes the string array, converts each entry into an Author instance, and even handles Realm primary key checks to avoid duplicate entries.
Step 1: Create the Custom Transform
Add this transform class to your project:
import ObjectMapper import RealmSwift class StringToAuthorListTransform: TransformType { // Define the types we're converting between typealias Object = List<Author> typealias JSON = [String] // Convert JSON string array to Realm List<Author> func transformFromJSON(_ value: Any?) -> List<Author>? { guard let authorNames = value as? [String] else { return nil } let authorsList = List<Author>() let realm = try? Realm() for name in authorNames { // Trim any whitespace (handles trailing commas/extra spaces in JSON) let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmedName.isEmpty else { continue } // Reuse existing author if it exists in Realm (using primaryKey) if let existingAuthor = realm?.object(ofType: Author.self, forPrimaryKey: trimmedName) { authorsList.append(existingAuthor) } else { let newAuthor = Author() newAuthor.authorName = trimmedName authorsList.append(newAuthor) } } return authorsList } // Convert Realm List<Author> back to string array (for serialization) func transformToJSON(_ value: List<Author>?) -> [String]? { return value?.compactMap { $0.authorName } } }
Step 2: Update the Book Mapping
Replace your existing authors mapping line in the Book class with this:
authors <- map[SerializationKeys.authors, StringToAuthorListTransform()]
Solution 2: Adjust the Author Class to Handle Raw Strings
If you prefer not to create a separate transform, you can modify your Author class to directly accept a string value instead of expecting a key-value pair.
Update the Author Class
import ObjectMapper import RealmSwift class Author: Object, Mappable { @objc dynamic var authorName: String? override class func primaryKey() -> String? { return "authorName" } convenience required init?(map: Map) { self.init() // Directly pull the string value from the map guard let name = map.currentValue as? String else { return nil } authorName = name.trimmingCharacters(in: .whitespacesAndNewlines) } func mapping(map: Map) { // Handle string input directly (no key needed) if let name = map.currentValue as? String { authorName = name.trimmingCharacters(in: .whitespacesAndNewlines) } } }
Use a Modified List Transform
Then, in your Book mapping, use a ListTransform with a custom closure to convert strings to Author instances:
authors <- map[SerializationKeys.authors, ListTransform<Author>(transform: { (value: Any?) -> Author? in guard let name = value as? String else { return nil } let author = Author() author.authorName = name.trimmingCharacters(in: .whitespacesAndNewlines) return author })]
Key Notes
- Avoid Duplicate Authors: Since
authorNameis your primary key, Solution 1 checks Realm for existing authors first—this prevents creating duplicateAuthorobjects for the same name. - Trim Whitespace: I added trimming to handle accidental spaces in the JSON (like the trailing comma in your example
["Name FirstName", ]). - Realm Write: Don't forget to wrap your save operation in a write block when mapping is done:
let realm = try! Realm() try! realm.write { realm.add(books, update: .modified) }
内容的提问来源于stack exchange,提问作者Kassy Barb

