Java Interop и макросы clojure

Я столкнулся со следующей проблемой. Я пытаюсь создать функцию (макрос) поверх API hapi-fhir.

function (macro) on topo of hapi-fhir api
  (defmacro search-patient-resource
  "This macro searches for a specified resource based on the
  Patient Id"
  [res id json?]
  (let [tmp (symbol res)]
    (if json?
         `(. (. (. (. (. (. @restful-client search) (forResource ~(symbol res))) encodedJson) (where (. (. ~(resolve tmp)  PATIENT)
                    (hasId (str ~id))))) (returnBundle Bundle)) execute)
    )))

Этот макрос работает, когда вы делаете что-то вроде

(let [id 10465]
 (search-patient-resource "Observation" id true))
=>#object[ca.uhn.fhir.model.dstu2.resource.Bundle 0x520a3cc9 "Bundle[id=Bundle/9ca62ae1-82af-488f-a166-5b014f45886e]"]

но не когда я делаю

 (let [id 10465 res "Observation"]
 (search-patient-resource "Observation" id true))
=> CompilerException java.lang.NullPointerException, compiling:(apycare_emrspp/hapi_fhir_helper.clj:122:1)

Конечно, я не могу написать (символ ~res), потому что тогда читатель оценивает (символ «Наблюдение») во время компиляции, и я получаю

 CompilerException java.lang.IllegalArgumentException: No matching method found: forResource for class ca.uhn.fhir.rest.client.GenericCl
 ient$SearchInternal, compiling:(apycare_emrspp/hapi_fhir_helper.clj:122:1)

Также ни

   (resolve (symbol ~res) 

ни

   (resolve ~(symbol re) 

работай.

Исходный код Java выглядит так

 FhirContext ctx = FhirContext.forDstu2();
 String serverBase = "fhirtest.uhn.ca/baseDstu2";
 IGenericClient client = ctx.newRestfulGenericClient(serverBase); 
 Bundle results = client .search() .forResource(Observation.class) 
.where(Observation.PATIENT.hasId("1234")) 
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) 
.execute(); 

Что я делаю, так это пытаюсь позвонить с помощью

 client
 .search() 
 .forResource(another-resource.class) 
 .where(another-resource.PATIENT.hasId(another-id)) 
 .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) 
 .execute();

person Heefoo    schedule 25.03.2017    source источник
comment
Я не понимаю, почему вы пишете это как макрос. Кажется, вы могли бы написать это как функцию и избежать проблемы.   -  person Alan Thompson    schedule 25.03.2017
comment
Как же так? Я пытался написать это как функцию, но как передать имя произвольного метода без жалоб компилятора clojure? Например, скажем, я хочу передать [Наблюдение], чтобы вызвать (Наблюдение за ресурсом). Если я передам его как строку, то (.ресурс (символ наблюдения)), не будет работать, если только он не расширяется из списка с помощью макросов.   -  person Heefoo    schedule 26.03.2017
comment
Уточните, пожалуйста, как это должно работать.   -  person Alan Thompson    schedule 26.03.2017
comment
При передаче строки и идентификатора в качестве аргументов строка должна быть преобразована в java-метод с тем же именем и вызвана. Это работает, но не тогда, когда я предопределяю строку. То есть, когда я передаю Observation, я (forResource ~(symbol res)) преобразуется в (forResource Observation) , но если я это делаю (def st Observation), то я получаю (forResource st).   -  person Heefoo    schedule 26.03.2017
comment
Я попытался создать функцию для создания символа из строки, но она не работает.   -  person Heefoo    schedule 26.03.2017
comment
Не могли бы вы изменить свой вопрос, чтобы показать эквивалентный код Java для того, что вы хотите сделать (т.е. как вызов lib будет выглядеть в Java)?   -  person Alan Thompson    schedule 28.03.2017
comment
Изменение идентификатора работает, как и ожидалось в макросе. Однако, когда я меняю наблюдение через предопределенную переменную, это не так. В другом сообщении предлагалось использовать другую функцию для создания списка, а затем объединять его в макросе, но это тоже не сработало. Я преодолел препятствие, проанализировав ответ json на вызов с помощью чистого clojure, но я все равно хотел бы знать, что происходит.   -  person Heefoo    schedule 29.03.2017
comment
Пожалуйста, используйте кнопку edit, чтобы изменить исходный вопрос, добавив код туда, а не в раздел Comments. Затем вы можете удалить комментарии (слишком тяжело читать здесь)   -  person Alan Thompson    schedule 29.03.2017


Ответы (1)


Ok. Проблема, с которой я столкнулся, была связана с тем, что я пренебрегал импортом соответствующих символов при вызове кода из другого пространства имен. когда res = "Resource" в приведенном выше коде, тогда

 ~(symbol "Resource")

Не удалось бы решить заранее, если бы я сначала не добавил

(import '(ca.uhn.fhir.model.dstu2.resource.Resource))  

В пространстве имен был вызван макрос. Это сделало код работающим в большинстве случаев. Чтобы сделать его полностью функциональным, мне пришлось изменить

 ~(symbol "Resource")

в (идентификация ~(символ "Ресурс"))

Это правильный перевод java

Resource.class в код clojure

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

 (defmacro search-patient-resource
 "This macro searches for a specified resource based on the
  Patient Id"
 [res id json?]
 (let [tmp (symbol res)]
   (if json?
     `(. (. (. (. (. (. @restful-client search)
                     (forResource (identity ~(symbol res))))
                  encodedJson)
               (where
                 (.
                  (. ~(resolve tmp)  PATIENT)
                  (hasId (~str ~id)))))
            (returnBundle Bundle))
         execute)
     `(. (. (. (. (. @restful-client search)
                  (forResource (identity ~(symbol res))))
               (where (. (. ~(symbol res)
                            PATIENT)
                         (hasId (str ~id)))))
            (returnBundle Bundle))
         execute))))
person Heefoo    schedule 14.04.2017