diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
index ebdcba08aeada56a34557f9f575329e539961f1c..777597b1e0dc6aa07d61c15b7a447700c09ed6a5 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.py
@@ -191,13 +191,22 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr
   if "." in select:
     select = select[select.rindex('.') + 1:]
 
+  # prepare accessor/getter name because this must be the first tried possibility
+  # getter is preferred way how to obtain properties - property itself is the second
+  if not select.startswith('get') and select[0] not in string.ascii_uppercase:
+    # maybe a hidden getter (variable accessible by a getter)
+    accessor_name = 'get' + UpperCase(select)
+  else:
+    # or obvious getter (starts with "get" or Capital letter - Script)
+    accessor_name = select
+
   # 1. resolve attribute on a raw object (all wrappers removed) using
   # lowest-level secure getattr method given object type
   raw_search_result = search_result
   if hasattr(search_result, 'aq_base'):
     raw_search_result = search_result.aq_base
-
-  if search_property_hasser(raw_search_result, select):
+  # BUT! only if there is no accessor (because that is the prefered way)
+  if search_property_hasser(raw_search_result, select) and not hasattr(raw_search_result, accessor_name):
     contents_value = search_property_getter(raw_search_result, select)
 
   # 2. use the fact that wrappers (brain or acquisition wrapper) use
@@ -207,12 +216,6 @@ def getAttrFromAnything(search_result, select, search_property_getter, search_pr
     unwrapped_search_result = search_result.aq_self
 
   if contents_value is None:
-    if not select.startswith('get') and select[0] not in string.ascii_uppercase:
-      # maybe a hidden getter (variable accessible by a getter)
-      accessor_name = 'get' + UpperCase(select)
-    else:
-      # or obvious getter (starts with "get" or Capital letter - Script)
-      accessor_name = select
     # again we check on a unwrapped object to avoid acquisition resolution
     # which would certainly find something which we don't want
     try:
diff --git a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
index 0d1b3be9a5581828d9c5508ef5054a7a29e2d21e..1488e122771a5d150c23302263cd86a596822463 100644
--- a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
+++ b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
@@ -990,7 +990,7 @@ return [Object(debit_price=1000.00, credit_price=100.00),
         Object(debit_price=10.00, credit_price=0.00)]
 """)
   @simulate('Test_listProducts', '*args, **kwargs', """
-return context.getPortalObject().foo_module.values()
+return context.getPortalObject().foo_module.contentValues()
 """)
   @simulate('Test_listCatalog', '*args, **kwargs', """
 return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id', 'ASC')])
@@ -1058,6 +1058,59 @@ return context.getPortalObject().portal_catalog(portal_type='Foo', sort_on=[('id
     self.assertEqual(result_dict['_embedded']['contents'][1]['getTotalQuantity'], 0)
 
 
+class TestERP5Person_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
+  """Test HAL_JSON operations on cataloged Persons and other allowed content types of Person Module."""
+
+  def afterSetUp(self):
+    self.person = self.portal.person_module.newContent(
+      portal_type='Person', first_name="Benoit", last_name="Mandelbrot")
+    self.tic()
+
+  def beforeTearDown(self):
+    self.portal.person_module.deleteContent(self.person.getId())
+
+
+  @simulate('Base_getRequestUrl', '*args, **kwargs', 'return "http://example.org/bar"')
+  @simulate('Base_getRequestHeader', '*args, **kwargs', 'return "application/hal+json"')
+  @simulate('Test_listPersons', '*args, **kwargs', """
+return context.getPortalObject().person_module.contentValues(portal_type="Person")
+""")
+  @simulate('Test_listPersonsCatalog', '*args, **kwargs', """
+return context.getPortalObject().portal_catalog.searchResults(portal_type="Person")
+""")
+  @changeSkin('Hal')
+  def test_getHateoas_person_title_search(self):
+    """Person has amazing property of having attribute "title" and "getTitle" with different return values.
+
+    Value resolution must prefer getter over raw attribute.
+    """
+    fake_request = do_fake_request("GET")
+
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
+      REQUEST=fake_request,
+      mode="search",
+      local_roles=["Assignor", "Assignee"],
+      list_method='Test_listPersons',
+      select_list=['title'] # attribute which must be resolved through getter
+    )
+    result_dict = json.loads(result)
+    titles = [result['title'] for result in result_dict['_embedded']['contents']]
+    # getTitle() composes title from first_name and last_name while attribute "title" remains empty
+    self.assertIn("Benoit Mandelbrot", titles)
+
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
+      REQUEST=fake_request,
+      mode="search",
+      local_roles=["Assignor", "Assignee"],
+      list_method='Test_listPersonsCatalog',
+      select_list=['title'] # attribute which must be resolved through getter
+    )
+    result_dict = json.loads(result)
+    titles = [result['title'] for result in result_dict['_embedded']['contents']]
+    # getTitle() composes title from first_name and last_name while attribute "title" remains empty
+    self.assertIn("Benoit Mandelbrot", titles)
+
+
 class TestERP5PDM_getHateoas_mode_search(ERP5HALJSONStyleSkinsMixin):
   """This class allows ticking for Movements to be picked up by activities."""