How to extend as.list in a canonical way to S4 objects

I am having trouble converting my S4 object to a list. Take, for example, the following nested S4 classes:

setClass("nssItem", 
         representation(value = "numeric", text = "character", prefix = "character", type = "character"),
         prototype(value = as.numeric(NA), text = as.character(NA), prefix = as.character(NA), type = as.character(NA))

         ) 

setClass("geckoNss", representation(absolute = "character", item = "nssItem"))

      

Class object geckoNss

contains class objects nssItem

. Conceptually, this is similar to a list-like structure that allows nesting.

Nevertheless,

> temp <- new("nssItem")
> as.list(temp)
Error in as.list.default(temp) : 
  no method for coercing this S4 class to a vector

      

I understand this error, meaning I have not actually defined what it means as.list

or how it relates to the class nssItem

. However, this seems like a very natural operation. How do I extend the definition as.list

to all new classes that I define?

+3


source to share


2 answers


This is the second, more general solution. It uses a superclass from which you get all custom classes. Descriptions are in the comments #

.

#this is an "empty" superclass that characterises all user-defined classes
setClass("user_defined_class")

#we create an as.list method for this new superclass (this time an S4 method)
setMethod("as.list",signature(x="user_defined_class"),function(x) {
  mapply(function(y) {
    #apply as.list if the slot is again an user-defined object
    #therefore, as.list gets applied recursively
    if (inherits(slot(x,y),"user_defined_class")) {
      as.list(slot(x,y))
    } else {
      #otherwise just return the slot
      slot(x,y)
    }
  },
slotNames(class(x)),
SIMPLIFY=FALSE)
})

setClass("nssItem", 
     representation(value = "numeric",
                    text = "character",
                    prefix = "character",
                    type = "character"),
     prototype(value = as.numeric(NA),
               text = as.character(NA),
               prefix = as.character(NA),
               type = as.character(NA)),
     #note the contains argument that flags the nssItem class as user-defined
     contains="user_defined_class")

setClass("geckoNss",
         representation(absolute = "character", item = "nssItem"),
         #the same for the geckoNss class
         contains="user_defined_class")

      

Now create one object for each class

temp <- new("nssItem")
tempGecko<-new("geckoNss")

      



Forcing temp

to display

as.list(temp)
#$value
#[1] NA
#
#$text
#[1] NA
#
#$prefix
#[1] NA
#
#$type
#[1] NA

      

And the object tempGecko

as.list(tempGecko)
#$absolute
#character(0)
#
#$item
#$item$value
#[1] NA
#
#$item$text
#[1] NA
#
#$item$prefix
#[1] NA
#
#$item$type
#[1] NA

      

+2


source


I'm not sure if I understood your "nesting" argument correctly, but there is code here on how to extend as.list

to your classes S4

. As Alex pointed out in the comments, these are actually S3

methods that are used on objects S4

. This works too. You can find a good summary on this topic here Combining S4 and S3 methods in one function

as.list.nssItem=function(from) mapply(function(x) slot(from,x),
                                      slotNames("nssItem"),
                                      SIMPLIFY=FALSE)

      

Now try as.list

on an nssItem

object temp

(as defined in your post)

as.list(temp)
#$value
#[1] NA
#
#$text
#[1] NA
#
#$prefix
#[1] NA
#
#$type
#[1] NA

      

Edit: I think now I understand what you mean by nesting. After executing the above code, define a new objectgeckoNss

tempGecko<-new("geckoNss")

      

Expand as.list

to classgeckoNss

as.list.geckoNss=function(from) mapply(function(x) {
  if (x=="item") as.list(slot(from,x)) else slot(from,x)
  },
  slotNames("geckoNss"),
  SIMPLIFY=FALSE)

      

Now apply as.list

to your objectgeckoNss

tempGecko



as.list(tempGecko)

#$absolute
#character(0)
#
#$item
#$item$value
#[1] NA
#
#$item$text
#[1] NA
#
#$item$prefix
#[1] NA
#
#$item$type
#[1] NA

      


As per Alex's comment below, here's a more general way to extend as.list

.

#save old function definition (just in case...)
as.list.default.save=as.list.default

      

Define a new default method

as.list.default=function(x) {
  if (class(x)=='list') {
    x
  } else if (class(x)%in%c('nssItem','geckoNss')) {
    mapply(function(slot_name) as.list(slot(x,slot_name)),
           slotNames(class(x)),
           SIMPLIFY=FALSE)
  } else {
    .Internal(as.vector(x, "list"))
  }
}

      

You still need to enter the vector of all your custom classes c('nssItem','geckoNss')

. I was unable to find a function that returns these classes. The result is not as well formed as above ...

as.list(temp)

#$value
#$value[[1]]
#[1] NA
#
#
#$text
#$text[[1]]
#[1] NA
#
#
#$prefix
#$prefix[[1]]
#[1] NA
#
#
#$type
#$type[[1]]
#[1] NA

      

+1


source







All Articles