---
schema_version: "1"
id: d3d83ba235a5de973128677ab621fb1c05b65731fde2f88da4a4ca53d86ae5fd
title: Output Metadata
collection: mulesoft
version: current
locale: en-us
portal_url: https://docs.salesforce.com/mulesoft/current/en-us/mule-sdk/latest/metadata-output.md
canonical_url: https://docs.mulesoft.com/mule-sdk/latest/metadata-output
source_site: docs.mulesoft.com
content_type: documentation
content_hash: 10af87f851add2bfaefb28dde99a0e1f17ddf25a33112fc71c5681e22281cc84
description: MuleSoft Documentation Site
release: "1.13"
latest: true
---
---
product: Mule SDK
version: 1.13
is-latest-version: true
---

# Output Metadata

> For the full documentation index, see: https://docs.mulesoft.com/llms.txt

Output metadata is the type resolution for the result of a component. Each component can provide
either static or dynamic metadata for the payload and attributes, isolated from each other.

## Declaring an OutputTypeResolver and AttributesTypeResolver

Both `OutputTypeResolver` and `AttributesTypeResolver` implementations handle the requests for
resolving a `MetadataType` based on the information provided by the `MetadataContext` and most
importantly, using the `MetadataKey` to identify the `MetadataType` required by the end user (who is
creating a Mule app). Refer to [What is the Metadata of a Component](https://docs.mulesoft.com/mule-sdk/latest/metadata#component-metadata)
for more information about the `MetadataType`.

The `OutputTypeResolver<T>` and `AttributesTypeResolver<T>` interfaces are parameterized with the
generic `T`, which must match the type of the `MetadataKeyId` parameter. The examples here use type
`String` for the generic because it is the most common `MetadataKeyId` type. (Other documentation
will revisit this generic and go in detail about when the type should change to something other than
`String`.)

```java
public class OutputEntityResolver
  implements OutputTypeResolver<String>, AttributesTypeResolver<String>  {

  @Override
  public String getCategoryName() {
    return "Records";
  }

  @Override
  public String getResolverName() {
    return "OutputEntityResolver";
  }

  @Override
  public MetadataType getOutputType(MetadataContext context, String key)
      throws MetadataResolvingException, ConnectionException {
    switch (key) {
      case "Author_id":
        return context.getTypeLoader().load(AuthorResult.class);
      case "BookList_id":
        return context.getTypeLoader().load(BookListResult.class);
      case "Book_id":
        return context.getTypeLoader().load(BookResult.class);
      default:
        throw new MetadataResolvingException("Unknown key:" + key, INVALID_METADATA_KEY);
    }
  }

  @Override
  public MetadataType getAttributesType(MetadataContext context, String key)
      throws MetadataResolvingException, ConnectionException {

    if ("Book_id".equals(key)){
      return context.getTypeLoader().load(BookAttributes.class);
    }

    // Only Books have Attributes information
    return context.getTypeBuilder().nullType().build();
  }

}
```

The example above uses the `MetadataContext` only to obtain the `typeLoader` for describing a
MetadataType based on a Java class, but it could also use the provided Configuration and Connection
elements.

## Using the OutputTypeResolver

After you have a `TypeKeysResolver` and an `OutputTypeResolver`, you can add output DataSense
support to our Operations and Sources. The main restriction to using an `OutputTypeResolver` with a
given `TypeKeysResolver` is that both must belong to the same `category`, which guarantees that the
`MetadataKey` provided by the `TypeKeysResolver` can be resolved to a `MetadataType` by the
`OutputTypeResolver`:

### Adding DataSense for the Result Payload

```java
public class FetchOperations {

  @OutputResolver(output = OutputEntityResolver.class)
  public Map<String,Object> get(@Connection MyConnection connection,
                                @MetadataKeyId(EntityKeysResolver.class) String entityKind){

    return connection.getClient().fetch(entityKind);
  }

}
```

Above, the MetadataType for the output is resolved dynamically based on the `entityKind` that is
being fetched, so if the `entityKind` is configured with the `Author_id` key provided by the
`EntityKeysResolver`, then the `OutputEntityResolver` will be invoked with that `MetadataKey`, and
the output for the `get` operation will be resolved to an `AuthorResult` ObjectType.

A source can use the same output type resolution for describing the MetadataType of the object being
dispatched to the flow, and its declaration is similar:

```java
@MetadataScope(keysResolver = EntityKeysResolver.class,
               outputResolver = OutputEntityResolver.class)
public class ListenerSource extends Source<Map<String, Object>, Void>  {

  @MetadataKeyId
  @Parameter
  public String type;

  @Connection
  private ConnectionProvider<MetadataConnection> connection;

  @Override
  public void onStart(SourceCallback<Map<String, Object>, Void> sourceCallback) throws MuleException {
    //...
  }

  @Override
  public void onStop() {
    //...
  }

}
```

Both sources and operations have the same lifecycle for resolving the output type. The first step is
to configure the `MetadataKeyId`, and then the `OutputTypeResolver` is invoked with that key in
order to resolve the resulting entity MetadataType.

### Adding DataSense for the Result Attributes

Dynamic metadata resolution takes into account that a component’s full output is a
`Result<Payload, Attributes>`, not just the payload.

When an operation or source output has a dynamic attributes structure, you can resolve its
MetadataType by declaring an `AttributesTypeResolver` (already implemented in the
`OutputTypeResolver.java` example) and then adding a reference to it in the operation or source
declaration.

In the next example, the `Book` entity can be divided into the content and attributes of the Book,
each with its own structures. This division is done so the end user can better understand and use
the result of the operation. They can think about the data (which is the content of the Book that
goes in the payload) and the metadata that characterizes the payload (the attributes of the Book):

```java
public class FetchOperationsWithAttributes {

  @OutputResolver(output = OutputEntityResolver.class,
                  attributes = OutputEntityResolver.class)
  public Result<Object, Object> get(@Connection MyConnection connection,
                                                @MetadataKeyId(EntityKeysResolver.class) String entityKind){

    if ("Book_id".equals(entityKind)){
      Book book = (Book)connection.getClient().fetch(entityKind);
      return Result.<Object, Object>builder()
                   .output(book.content())
                   .attributes(book.attributes())
                   .build();
    }

    return Result.<Object, Object>builder()
                 .output(connection.getClient().fetch(entityKind))
                 .build();
  }

}
```

Sources have a declaration similar to the one used for the payload, but it adds an
`attributesResolver` reference:

```java
@MetadataScope(keysResolver = EntityKeysResolver.class,
               outputResolver = OutputEntityResolver.class,
               attributesResolver = OutputEntityResolver.class)
public class ListenerSource extends Source<Map<String, Object>, Object>  {

  @MetadataKeyId
  @Parameter
  public String type;

  //...

}
```

## Output Metadata with a User-Defined MetadataKey

The case for user-defined MetadataKeys also applies for the output of a component. In the case of a
query, you do not have a predefined set of possible MetadataKeys. Instead, you have a parameter with
a value that characterizes the output type or structure.

For example, the Database connector has the `select` operation. Its output depends on what entities
are queried:

```java
  @OutputResolver(output = SelectMetadataResolver.class)
  public List<Map<String, Object>> select(@MetadataKeyId String sql, @Config DbConnector connector){
    // ...
  }
```

With the `SelectMetadataResolver` declared as:

```java
public class SelectMetadataResolver extends BaseDbMetadataResolver implements OutputTypeResolver<String> {

  @Override
  public String getCategoryName() {
    return "DbCategory";
  }

  @Override
  public String getResolverName() {
    return "SelectResolver";
  }

  @Override
  public MetadataType getOutputType(MetadataContext context, String query)
      throws MetadataResolvingException, ConnectionException {

    if (isEmpty(query)) {
      throw new MetadataResolvingException("No Metadata available for an empty query", FailureCode.INVALID_METADATA_KEY);
    }

    ResultSetMetaData statementMetaData = getStatementMetadata(context, parseQuery(query));
    if (statementMetaData == null) {
      throw new MetadataResolvingException(format("Driver did not return metadata for the provided SQL: [%s]", query),
                                           FailureCode.INVALID_METADATA_KEY);
    }

    ObjectTypeBuilder record = context.getTypeBuilder().objectType();

    Map<String, MetadataType> recordModels = resolveRecordModels(statementMetaData);
    recordModels.entrySet()
                .forEach(e -> record.addField().key(e.getKey()).value(e.getValue()));

    return record.build();
  }
}
```

## List Metadata Automatic Wrapping

In the `select` example, you can see that the operation returns a `List<Map<String, Object>`. This
makes sense because the result of a select query is multiple record entries, but the
`SelectMetadataResolver` does not describe an ArrayType in the `getOutputType` method. Instead the
returned MetadataType represents a single `record` structure.

Why is that?

As you might know already, the operation is returning an ArrayType (List, PagingProvider, and so
on), so you only need to describe the `generic` type of the array. The output and attributes
TypeResolvers always resolve the MetadataType of _the elements of the collection_ and not the
_collection_ type itself. This allows for greater reuse of the MetadataType resolvers and reduces
the amount of code needed.

Take into account that the resolved attributes will _also_ be the attributes of the _elements_ of
the collection, _not_ attributes of the operation’s `List` output.

## Resolving Dynamic Output Metadata without MetadataKey

As with input, the output of an operation can be resolved without a specific `MetadataKey`, being
the dynamic type affected by the Configuration or Connection of the Component. Again, to declare a
keyless resolver, you simply skip the `MetadataKeyId` parameter and ignore the MetadataKey in the
TypeResolvers:

```java
public class UserTypeResolver implements OutputTypeResolver, AttributesTypeResolver  {

  @Override
  public String getCategoryName() {
    return "User";
  }

  @Override
  public MetadataType getOutputType(MetadataContext context, Object key)
      throws MetadataResolvingException, ConnectionException {

    // The `key` parameter will be `null` if the fetch is performed
    // as a `KeyLess` Metadata resolution. We'll just ignore it.
    String schema = getUserSchema(context);
    return new JsonTypeLoader(schema).load("http://demo.user")
            .orElseThrow(() -> new MetadataResolvingException("No Metadata is available for the User",
                                                              FailureCode.NO_DYNAMIC_TYPE_AVAILABLE));
  }

  @Override
  public MetadataType getAttributesType(MetadataContext context, Object key)
      throws MetadataResolvingException, ConnectionException {

    // The `key` parameter will be `null` if the fetch is performed
    // as a `KeyLess` Metadata resolution. We'll just ignore it.
    String schema = getUserSchema(context);
    return new JsonTypeLoader(schema).load("http://demo.attributes")
            .orElseThrow(() -> new MetadataResolvingException("No Metadata is available for the User Attributes",
                                                              FailureCode.NO_DYNAMIC_TYPE_AVAILABLE));
  }

  private String getUserSchema(MetadataContext context) throws MetadataResolvingException, ConnectionException {
    return context.<DemoConnection>getConnection()
      .orElseThrow(() -> new MetadataResolvingException("A connection is required to resolve Metadata but none was provided",
                                                        FailureCode.INVALID_CONFIGURATION))
      .describeUser();
  }
}
```

```java
public class UserOperations {

  @OutputResolver(output = UserTypeResolver.class, attributes=UserTypeResolver.class)
  public Result<Map<String,Object>, Object> getUser(@Connection DemoConnection connection){
    User user = connection.getUser();

    return Result.<Map<String,Object>, Object>.builder()
                 .output(user.personalInfo())
                 .attributes(user.accountInfo())
                 .build().

  }

}
```
