Cristian E.
Cristian E.

Reputation: 3583

Odata v4 - $expand then flattern result

Objective: To expand an object, and project a nested property, onto the root selection, alongside with other props.

Having the following relationship:

public class Product {
   public string Barcode { get; set; }
   public double Price { get; set; }
   public Category Category { get; set; }
}

public class Category {
   public string Name { get; set; }
}

I would like to make a projection which will result in this:

{
   "@odata.context": "http://localhost/odata/$metadata#Product",
   "value": [
      {
         "Price": 500,
         "Name": "Meat Products" // this is category name, ideally would be to rename it to CategoryName
      }
   ]
}

Where as currently I get this:

{
   "@odata.context": "http://localhost/odata/$metadata#Product",
   "value": [
      {
         "Price": 500,
         "Category": {
            "Name": "Meat Products"
         }
      }
   ]
}

The query used is the following:

/odata/Product?$expand=Category($select=Name)&$select=Price

I would expect to write a projection like this:

/odata/Product?$expand=Category&$select=Price,Category/Name as CategoryName

or

/odata/Product?$expand=Category&$select=Price,Category($select=Name as CategoryName)

or

/odata/Product?$expand=Category&$select=Price,Category($select=Name)

Is that achievable ? Thank you!

P.S. OData V4.

Upvotes: 1

Views: 2830

Answers (2)

HisDivineShadow
HisDivineShadow

Reputation: 1148

With the latest version of OData you can use the compute function to flatten the response. Lets say that you have a class with an navigation property like below:

public class A {
 public string name { get; set; }
 public bId int  { get; set; }
 public virtual B test  { get; set; }
}

public class B {
 public Id int  { get; set; }
 public string field1 { get; set; }
 public string field2 { get; set; }
}

Then your OData query would look something like this: /odata/A?$compute=substring(test/field2,0) as testfield2&$select=name,testfield2

and it will return this

"@odata.context": "https://localhost/$metadata#A(name,testfield2)",
"value": [
    {
        "name": 99,
        "testfield2": "Storage"
    },
    {
        "name": 100,
        "testfield2": "Installed - New"
    },
]

However once you start using select you must then select all of the fields you want in the output.

Upvotes: 0

Karata
Karata

Reputation: 1149

This is not achievable with odata v4 query semantic. As you can see, the response body contains a line:

"@odata.context": "http://localhost/odata/$metadata#Product"

This is to indicate the whole response payload represents an instance of 'Product' type. Suppose 'CategoryName' property does not exist on that type, it is not possible to instruct service to dynamically add one via the 'AS' clause. And keyword 'AS' also does not exist in standard OData query spec.

However, it is indeed valid to return an additional property beyond the metadata, see Reference.

Clients MUST be prepared to receive additional properties in an entity or complex type instance that are not advertised in metadata, even for types not marked as open.

So in this case, the service could just return an additional 'virtual' property 'CategoryName' in the response. (If you're the service owner you can update response logic and do the change.) This could be a service behavior, rather than a reaction to certain client query.

Upvotes: 1

Related Questions