Повторение собственных свойств объекта в Groovy?

#groovy

#groovy

Вопрос:

Я создал класс, который позволяет результирующему объекту добавлять к нему произвольные свойства. Класс также имеет некоторые предопределенные свойства. В методе класса я хочу иметь возможность выполнять итерации по всем свойствам, принадлежащим экземпляру объекта.

Вот пример определения класса:

 import groovy.json.*

class Foo {
    private Map props = [:]

    String bar = "baz"
    def myNumber = 42

    void propertyMissing(String name, Object value) {
        this.props[name] = value
    }

    def propertyMissing(String name) {
        return this.props[name]
    }

    def toJsonString() {
        def outObject = [:]

        // I want to do something like this
        this.properties.each { k, v ->
            if (this.isOwnProperty(k) amp;amp; k != 'props') {
                outObject[k] = v
            }
        }

        outObject = outObject   this.props

        return JsonOutput.toJson(outObject)
        // Should return a string like:
        // {"bar":"baz", "myNumber":42, "someDynamicProperty":"value"}
        //
        // This string should not contain the "class" and "metaClass"
        // properties.
    }
}
 

Есть ли способ сделать то, что я хочу сделать?

Редактировать: одна из моих целей — не указывать явно мои предопределенные свойства в toJsonString методе. Я хочу иметь возможность добавлять новые предопределенные свойства позже, не забывая обновлять toJsonString метод.

Редактировать (24 октября 2011):

Принятый ответ дал мне необходимую информацию. Тем не менее, мне все равно потребовалось назвать свойства, которые я не хочу включать в строку JSON. Расширение ответа немного решает эту проблему:

 def outObject = Foo.declaredFields.findAll {
    // 'it' is a Field object returned by
    // http://download.oracle.com/javase/1,5,0/docs/api/java/lang/Class.html#getDeclaredFields()
    !it.synthetic amp;amp;
    it.getModifiers() != java.lang.reflect.Modifier.PRIVATE
}.collectEntries { v ->
    [ (v.name) : this[v.name] ]
}
 

Чтобы это сработало, вы должны явно указать модификаторы для свойств вашего класса. Это String bar = "baz" в моем примере должно быть public String bar = "baz" для того, чтобы оно было включено в строку JSON.

Ответ №1:

Существует такая возможность (при условии, что у меня есть правильный конец палки) 😉

 class Foo {
    private Map props = [:]

    String bar = "baz"
    def myNumber = 42

    void propertyMissing(String name, Object value) {
        this.props[name] = value
    }

    def propertyMissing(String name) {
        return this.props[name]
    }

    def toJsonString() {
        def outObject = Foo.declaredFields.findAll { !it.synthetic amp;amp; it.name != 'props' }.collectEntries { v ->
          [ (v.name):this[v.name] ]
        }
        outObject << props
        JsonOutput.toJson(outObject)
    }
}
 

Если у вас нет Groovy 1.7.9 , то строки

         def outObject = Foo.declaredFields.findAll { !it.synthetic amp;amp; it.name != 'props' }.collectEntries { v ->
          [ (v.name):this[v.name] ]
        }
 

Следует заменить на:

         def outObject = Foo.declaredFields.findAll { !it.synthetic amp;amp; it.name != 'props' }.inject([:]) { m, v ->
          m << [ (v.name):this[v.name] ]
        }
 

И я считаю, что он будет вести себя так же; т.Е.: если я сделаю это:

 def f = new Foo()
f.tim = 'yates'
println f.toJsonString()
 

он распечатывает:

 {"bar":"baz","myNumber":42,"tim":"yates"}
 

Комментарии:

1. Спасибо. Это именно то, что я хочу сделать.

Ответ №2:

Если вы хотите придерживаться этой реализации, возможно, переопределение getProperties для включения вашей карты свойств было бы проще всего.

Комментарии:

1. Если я правильно понимаю ваше предложение, стандартный fooInstance.properties() метод уже включает (не совсем) частную карту. Я обновил свой вопрос дополнительной информацией о своей цели.

2. @jsumners Он включает карту, но как карту, а не ее записи как свойства «верхнего уровня». Я говорю о создании a getProperties , которое не включает map-as-map, но вместо этого предоставляет ключи map в качестве имен свойств, а значения — в качестве значений этих свойств.

3. Ладно. В этом есть смысл. Однако, когда я def getProperties(){ return this.props.keySet() } затем пытаюсь установить динамическое свойство, появляется ошибка «Такого свойства нет». Кроме того, я бы все равно хотел исключить унаследованные свойства, такие как «класс» и «метаКласс», без необходимости знать их все заранее. Этот ответ не касается этой части вопроса.

4. Вы не можете просто вернуть this.props.keySet() , потому что тогда вы пропустите фактические свойства; вот почему я сказал включить записи карты свойств. Список свойств метакласса короткий и известный, или вы можете явно удалить свойства супер / мета-класса, отфильтровав super.properties . Но рассматривали ли вы возможность просто использовать ExpandoMetaClass? Вот почему я начал с «придерживаться этой реализации», поскольку у Groovy уже есть способ по умолчанию представлять класс, свойства которого неизвестны до времени выполнения.

5. Теперь я вижу, что неправильно прочитал документацию, когда пытался просто вернуть набор ключей; это был долгий день. Тем не менее, у меня все еще возникают проблемы с этим, так как теперь я, похоже, не могу создавать экземпляры groovy.lang. Метаобъект. Интерпретатор либо ищет метод MetaProperty в моем объекте «Foo», либо не может найти «lang» в объекте «groovy» (т.е. groovy.lang.MetaProperty ). Позвольте мне внести ясность: я не знаю Groovy. Реальный пример принесет мне большую пользу.