CDATA deserialization works great out of the box. But how should I configure this mapping:
class MyData: XMLMappable {
var nodeName: String!
var cdataValue: String?
...
func mapping(map: XMLMap) {
cdataValue <- map.attributes["cdataValue"]
}
}
and when I call toXMLString():
let myData = MyData()
myData.cdataValue = "actualValue"
print(myData.toXMLString())
to eventually obtain something like this:
<cdataValue><![CDATA[ actualValue ]]></cdataValue>
Related
I have the following XML string. I am trying to parse with XMLMapper (3rd party library).
I need to create array of
The xml is as follow;
<RandomDecimals>
<decimal>98258979</decimal>
<decimal>1000002218</decimal>
<decimal>1000002102</decimal>
<decimal>1000005713</decimal>
<decimal>1000002035</decimal>
<decimal>1000001434</decimal>
<decimal>1000001066</decimal>
<decimal>1000001498</decimal>
<decimal>1000002313</decimal>
<decimal>1000016914</decimal>
<decimal>1000017021</decimal>
<decimal>1000019039</decimal>
<decimal>52373625</decimal>
</RandomDecimals>
The code to parse to object is as follows;
class DecimalElement: XMLMappable {
var nodeName: String!
var decimal: String?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
decimal <- map["decimal"]
}
}
class Response: XMLMappable {
var nodeName: String!
var shouldReset: Bool!
var randomDecimals: [DecimalElement]?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
randomDecimals <- map["RandomDecimals.decimal"]
shouldReset <- map["ShouldReset"]
}
As a summary, I need to take decimals into randomDecimals array. But the I need to do it with XMLMapper.
What am I doing wrong?
BR,
Erdem
If your XML looks something like this:
<root>
<ShouldReset>true</ShouldReset>
<RandomDecimals>
<decimal>98258979</decimal>
<decimal>1000002218</decimal>
<decimal>1000002102</decimal>
<decimal>1000005713</decimal>
<decimal>1000002035</decimal>
<decimal>1000001434</decimal>
<decimal>1000001066</decimal>
<decimal>1000001498</decimal>
<decimal>1000002313</decimal>
<decimal>1000016914</decimal>
<decimal>1000017021</decimal>
<decimal>1000019039</decimal>
<decimal>52373625</decimal>
</RandomDecimals>
</root>
then, you can solve your issue by replacing:
var randomDecimals: [DecimalElement]?
with:
var randomDecimals: [String]?
in your Response class.
class Response: XMLMappable {
var nodeName: String!
var shouldReset: Bool!
var randomDecimals: [String]?
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
randomDecimals <- map["RandomDecimals.decimal"]
shouldReset <- map["ShouldReset"]
}
}
You can also use Array of Int64 to map directly the decimal numbers:
var randomDecimals: [Int64]?
Hope this helps.
I have an array of objects I'm trying to get out of one of my collections. I've followed along using their docs and also some Googling and I believe I'm close to the solution, however not close enough. Here's what I have:
class Clothing: Entity {
var categories: [Category]!
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "categories"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- map["clothing"]
gender <- map["gender"]
}
}
class Category: NSObject, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- map["category"]
image <- map["image"]
}
}
I'm able to get the right gender, but the array of categories doesn't seem to get mapped to the Category object. Any thoughts?
your model actually have one issue, as you can see at https://devcenter.kinvey.com/ios/guides/datastore#Model you should use let categories = List<Category>() instead of var categories: [Category]!. Here's the model that and test and worked:
import Kinvey
class Clothing: Entity {
let categories = List<Category>()
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "clothing"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- ("categories", map["categories"])
gender <- ("gender", map["gender"])
}
}
class Category: Object, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- ("category", map["category"])
image <- ("image", map["image"])
}
}
and here's a sample code how to save a new Clothing object
let casualCategory = Category()
casualCategory.title = "Casual"
let shirtCategory = Category()
shirtCategory.title = "Shirt"
let clothing = Clothing()
clothing.gender = "male"
clothing.categories.append(shirtCategory)
clothing.categories.append(casualCategory)
dataStore.save(clothing) { (result: Result<Clothing, Swift.Error>) in
switch result {
case .success(let clothing):
print(clothing)
case .failure(let error):
print(error)
}
}
What I need to represent in RealmSwift is the following JSON scheme:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
Its a basic string array that I'm stumbling on. I'm guessing in Realm I need to represent "tags" as
dynamic id: Int = 0
dynamic title: String = ""
let tags = List<MyTagObject>()
making tags its own table in Realm, but how to map it with ObjectMapper? This is how far I got...
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- map["tags"]
}
... but the tags line doesn't compile of course because of the List and Realm cannot use a [String] type.
This feels like a somewhat common problem and I'm hoping someone who has faced this can comment or point to a post with a suggestion.
UPDATE 1
The MyTagObject looks like the following:
class MyTagObject: Object {
dynamic var name: String = ""
}
UPDATE 2
I found this post which deals with the realm object but assumes the array has named elements rather than a simple string.
https://gist.github.com/Jerrot/fe233a94c5427a4ec29b
My solution is to use an ObjectMapper TransformType as a custom method to map the JSON to a Realm List<String> type. No need for 2 Realm models.
Going with your example JSON:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
First, create an ObjectMapper TransformType object:
import Foundation
import ObjectMapper
import RealmSwift
public struct StringArrayTransform: TransformType {
public init() { }
public typealias Object = List<String>
public typealias JSON = [String]
public func transformFromJSON(_ value: Any?) -> List<String>? {
guard let value = value else {
return nil
}
let objects = value as! [String]
let list = List<String>()
list.append(objectsIn: objects)
return list
}
public func transformToJSON(_ value: Object?) -> JSON? {
return value?.toArray()
}
}
Create your 1 Realm model used to store the JSON data:
import Foundation
import RealmSwift
import ObjectMapper
class MyObjectModel: Object, Mappable {
#objc dynamic id: Int = 0
#objc dynamic title: String = ""
let tags = List<MyTagObject>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- (map["tags"], StringArrayTransform())
}
}
Done!
This line is the magic: tags <- (map["tags"], StringArrayTransform()). This tells ObjectMapper to use our custom StringArrayTransform I showed above which takes the JSON String array and transforms it into a Realm List<String>.
First of all we should assume that our model extends both Object and Mappable.
Let's create a wrapper model to store the primitive (String) type:
class StringObject: Object {
dynamic var value = ""
}
Then we describe corresponding properties and mapping rules for the root model (not the wrapper one):
var tags = List<StringObject>()
var parsedTags: [String] {
var result = [String]()
for tag in tags {
result.append(tag.value)
}
return result
}
override static func ignoredProperties() -> [String] {
return ["parsedTags"]
}
func mapping(map: Map) {
if let unwrappedTags = map.JSON["tags"] as? [String] {
for tag in unwrappedTags {
let tagObject = StringObject()
tagObject.value = tag
tags.append(tagObject)
}
}
}
We need a tags property to store and obtain the data about tags from Realm.
Then a parsedTags property simplifies extraction of tags in the usual array format.
An ignoredProperties definition allows to avoid some failures with Realm while data savings (because of course we can't store non-Realm datatypes in the Realm).
And at last we are manually parsing our tags in the mapping function to store it in the Realm.
It will work if your tags array will contains a Dictionary objects with a key: "name"
{
"id": 1234,
"title": "some value",
"tags": [ ["name" : "red"], ... ]
}
If you cannot modify JSON object, I recommend you to map json to realm programmatically.
for tagName in tags {
let tagObject = MyTagObject()
tagObject.name = tagName
myObject.tags.append(tagObject)
}
Follow this code
import ObjectMapper
import RealmSwift
//Create a Model Class
class RSRestaurants:Object, Mappable {
#objc dynamic var status: String?
var data = List<RSData>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
status <- map["status"]
data <- (map["data"], ListTransform<RSData>())
}
}
//Use this for creating Array
class ListTransform<T:RealmSwift.Object> : TransformType where T:Mappable {
typealias Object = List<T>
typealias JSON = [AnyObject]
let mapper = Mapper<T>()
func transformFromJSON(_ value: Any?) -> Object? {
let results = List<T>()
if let objects = mapper.mapArray(JSONObject: value) {
for object in objects {
results.append(object)
}
}
return results
}
func transformToJSON(_ value: Object?) -> JSON? {
var results = [AnyObject]()
if let value = value {
for obj in value {
let json = mapper.toJSON(obj)
results.append(json as AnyObject)
}
}
return results
}
}
I have an class:
class ChatMessage: Object, Mappable {
dynamic var fromId = ""
dynamic var toId = ""
dynamic var message = ""
dynamic var fromName = ""
dynamic var created: Int64 = 0
required convenience init?(map: Map) {
self.init()
}
func configure(_ fromId:String,toId:String, message:String) {
self.fromId=fromId
self.toId=toId
self.message=message
self.created = Int64((NSDate().timeIntervalSince1970 * 1000.0))
}
func mapping(map: Map) {
created <- map["created"] //a: this was added later
fromId <- map["fromId"]
toId <- map["toId"]
message <- map["message"]
fromName <- map["fromName"]
}
}
I am using ObjectMapper to serialise the object to JSON and Realm to store it in the local database.
I had added the created field later to the mapping when the Realm db was already storing the ChatMessage object.
Now when I am instantiating the ChatMessage object and trying to convert it into JSON object using ObjectMapper. Following is the code:
func sendChatMessage(_ chatMessage:ChatMessage, callback: #escaping DataSentCallback) -> Void {
var chatMessageString:String!
let realm = DBManager.sharedInstance.myDB
try! realm?.write {
chatMessageString = Mapper().toJSONString(chatMessage, prettyPrint: false)!
}
...
}
Now when I print chatMessage, I get:
ChatMessage {
fromId = 14;
toId = 20;
message = 2;
fromName = ;
created = 1477047392597;
}
And when I print chatMessageString, I get:
"{\"toId\":\"20\",\"message\":\"2\",\"fromName\":\"\",\"fromId\":\"14\"}"
How come does the created field not appear in the string?
The problem was in the mapping of Int64 type as mentioned in this issue on github.
By changing the mapping of created to the following form, everything worked fine:
created <- (map["created"], TransformOf<Int64, NSNumber>(fromJSON: { $0?.int64Value }, toJSON: { $0.map { NSNumber(value: $0) } }))
I am using Alamofire with ObjectMapper and my model class is like that
class Category: Object, Mappable {
dynamic var id: Int = 0
dynamic var name = ""
dynamic var thumbnail = ""
var children = List<Category>()
override static func primaryKey() -> String? {
return "id"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
thumbnail <- map["thumbnail"]
children <- map["children"]
}
}
and I am using Alamofire like that
Alamofire.request(.GET, url).responseArray { (response: Response<[Category], NSError>) in
let categories = response.result.value
if let categories = categories {
for category in categories {
print(category.id)
print(category.name)
}
}
}
the id is always zero, I don't know why?
I fixed it by adding transformation in the mapping function in model class like that
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
thanks to #BobWakefield
Does the "id" field exist in the JSON file? If it does not, your initial value of zero will remain. Is the value in quotes in the JSON file? If it is, then it's a string. I don't know if ObjectMapper will convert it to Int.
Moved my comment to an answer.