выражение spel интеграции spring для извлечения полей из полезной нагрузки сообщения

#java #spring-boot #spring-integration #spring-expression-language

#java #spring-boot #spring-интеграция #spring-expression-language

Вопрос:

Не могу разобраться в SPEL для полезных нагрузок сообщений. Я хочу извлечь данные из определенных полей моей полезной нагрузки сообщения, которые по сути представляют собой следующий JSON, преобразованный Map<String, Object> и переданный в @Transformer

 {
  "expand":"renderedFields,names,schema,transitions,operations,editmeta,changelog,versionedRepresentations",
  "id":"14730",
  "self":"https://jira.foo.com/rest/api/2/issue/14730",
  "key":"SDP-145",
  "fields":{
    "issuetype":{
      "self":"https://jira.foo.com/rest/api/2/issuetype/10200",
      "id":"10200",
      "description":"gh.issue.epic.desc",
      "iconUrl":"https://jira.foo.com/ghanghor/viewkaka?size=xsmallamp;kakaId=10501amp;kakaType=issuetype",
      "name":"Epic",
      "subtask":false,
      "kakaId":10501
    },
    "priority":{
      "self":"https://jira.foo.com/rest/api/2/priority/3",
      "iconUrl":"https://jira.foo.com/images/icons/priorities/major.svg",
      "name":"Major",
      "id":"3"
    },
    "labels":[
      "Lizzy",
      "kanban",
      "rughani"
    ],
    "updated":"2021-01-21T10:33:38.000 0000",
    "status":{
      "self":"https://jira.foo.com/rest/api/2/status/1",
      "description":"The issue is open and ready for the assignee to start work on it.",
      "iconUrl":"https://jira.foo.com/images/icons/statuses/open.png",
      "name":"Open",
      "id":"1",
      "statusCategory":{
        "self":"https://jira.foo.com/rest/api/2/statuscategory/2",
        "id":2,
        "key":"new",
        "colorName":"blue-gray",
        "name":"To Do"
      }
    },
    "summary":"new epic for Tazzy",
    "creator":{
      "self":"https://jira.foo.com/rest/api/2/user?username=skadmin",
      "name":"skadmin",
      "key":"skadmin",
      "emailAddress":"Lizzy.t@foo.com",
      "displayName":"Lizzy Rughani",
      "active":true,
      "timeZone":"Asia/Kolkata"
    },
    "subtasks":[

    ],
  }
}
 

Здесь меня интересуют три вложенных значения, которые я пытаюсь получить с помощью следующих выражений

 issueDataMap = {LinkedHashMap@4867}  size = 3
 "name" -> "#payload['fields']['summary']"
 "description" -> "#payload['description']"
 "text3" -> "#payload['key']"
 

Я получаю эту ошибку при применении выражения

 org.springframework.expression.spel.SpelEvaluationException: EL1012E: Cannot index into a null value
 

Вот как я получаю полезную нагрузку в качестве аргумента для моего трансформатора

 @Transformer
public Map<String, Object> generateCardData(Map<String, Object> payload,
                                            @Header("X-UPSTREAM-WEBHOOK-SOURCE") String projectId) {
 

затем

 StandardEvaluationContext evaluationContext = evaluationContextFactory.getObject();
 

и вот как я это оцениваю

 new SpelExpressionParser().parseExpression(issueDataMap.get(key)).getValue(
                            evaluationContext, payload, String.class)));
 

У меня есть приложение, снабженное аннотациями @SpringBootApplication и @EnableInegration , и я автоматически подключаю экземпляр IntegrationEvaluationContextFactoryBean , чтобы получить StandardEvaluationContext

Я также попробовал вариант

 issueDataMap = {LinkedHashMap@4867}  size = 3
 "name" -> "payload['fields']['summary']"
 "description" -> "payload['description']"
 "text3" -> "payload['key']"
 

но тогда я получаю

 EL1008E: Property or field 'payload' cannot be found on object of type 'java.util.LinkedHashMap' - maybe not public or not valid?
 

Ответ №1:

Прежде всего, неясно, зачем использовать SpEL в коде вручную, когда у вас есть полный доступ к объекту. Кроме того, вы должны иметь в виду, что создание StandardEvaluationContext , анализ выражения и оценка его при каждом отдельном вызове — это своего рода накладные расходы по производительности. Вероятно, вам просто нужно изменить свою generateCardData() подпись, чтобы принять результат выражения вместо всей карты. См @Payload.expression . Атрибут.

В любом случае, это не то, что вы хотели бы услышать о своей проблеме. И это здесь: getValue(evaluationContext, payload, String.class))) . Корневым объектом оценки является ваш payload — a Map . Итак, вам просто нужно предположить в вашем определении выражения, что вы получаете доступ к этому корневому объекту. Поэтому выражения должны быть такими: fields.summary , description , key .

Обычно вы видите в документах и примерах payload (или header ) в качестве первого токена в выражении. Это просто потому, что Spring Integration использует a Message в качестве корневого объекта для вычисления выражений.

Теперь в отношении производительности. Даже если ваша логика выбирает выражение по некоторому ключу во время выполнения ( issueDataMap.get(key) ), вы все равно можете проанализировать его только один раз.

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

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

2. Понятно. В любом случае, это issueDataMap может быть связано с предварительно проанализированными выражениями. Что-нибудь еще не ясно из моего ответа?

3. Да, я заметил, что я бы провел рефакторинг, перейдя к исходному вопросу, ясно, что я неправильно истолковал язык выражений.