XMLtoJSON policy

Standard policy

This page applies to Apigee and Apigee hybrid.

View Apigee Edge documentation.

The XMLtoJSON policy converts messages from the extensible markup language (XML) format to JavaScript Object Notation (JSON), giving you several options for controlling how messages are converted.

Assuming that the intent is to convert an XML-formatted response into a JSON-formatted response, the policy would be attached to a response Flow (for example, Response / ProxyEndpoint / PostFlow).

In a typical mediation scenario, a JSON to XML policy on the inbound request flow is often paired with an XML to JSON policy on the outbound response flow. By combining policies this way, a JSON API can be exposed for backend services that natively support only XML.

For scenarios where APIs are consumed by diverse client apps that may require either JSON or XML, the response format can be dynamically set by configuring JSON to XML and XML to JSON policies to execute conditionally. See Flow variables and conditions for an implementation of this scenario.

This policy is a Standard policy and can be deployed to any environment type. For information on policy types and availability with each environment type, see Policy types.

Samples

For a detailed discussion on converting between JSON and XML, see this article.

Converting a response

<XMLToJSON name="ConvertToJSON">
  <Options>
  </Options>
  <OutputVariable>response</OutputVariable>
  <Source>response</Source>
</XMLToJSON>

This configuration—which is the minimal configuration required to convert XML to JSON—takes an XML-formatted response message as the source, and then creates a JSON-formatted message that is populated in the response OutputVariable. Apigee automatically uses the content of this variable as the message for next processing step.

Element reference

Following are elements and attributes you can configure on this policy.

<XMLToJSON async="false" continueOnError="false" enabled="true" name="XML-to-JSON-1">
    <DisplayName>XML to JSON 1</DisplayName>
    <Source>response</Source>
    <OutputVariable>response</OutputVariable>
    <Options>
        <RecognizeNumber>true</RecognizeNumber>
        <RecognizeBoolean>true</RecognizeBoolean>
        <RecognizeNull>true</RecognizeNull>
        <NullValue>NULL</NullValue>
        <NamespaceBlockName>#namespaces</NamespaceBlockName>
        <DefaultNamespaceNodeName>&</DefaultNamespaceNodeName>
        <NamespaceSeparator>***</NamespaceSeparator>
        <TextAlwaysAsProperty>true</TextAlwaysAsProperty>
        <TextNodeName>TEXT</TextNodeName>
        <AttributeBlockName>FOO_BLOCK</AttributeBlockName>
        <AttributePrefix>BAR_</AttributePrefix>
        <OutputPrefix>PREFIX_</OutputPrefix>
        <OutputSuffix>_SUFFIX</OutputSuffix>
        <StripLevels>2</StripLevels>
        <TreatAsArray>
            <Path unwrap="true">teachers/teacher/studentnames/name</Path>
        </TreatAsArray>
    </Options>
    <!-- Use Options or Format, not both -->
    <Format>yahoo</Format>
</XMLToJSON>

<XMLtoJSON> attributes

<XMLtoJSON async="false" continueOnError="false" enabled="true" name="XML-to-JSON-1">

The following table describes attributes that are common to all policy parent elements:

Attribute Description Default Presence
name

The internal name of the policy. The value of the name attribute can contain letters, numbers, spaces, hyphens, underscores, and periods. This value cannot exceed 255 characters.

Optionally, use the <DisplayName> element to label the policy in the management UI proxy editor with a different, natural-language name.

N/A Required
continueOnError

Set to false to return an error when a policy fails. This is expected behavior for most policies.

Set to true to have flow execution continue even after a policy fails. See also:

false Optional
enabled

Set to true to enforce the policy.

Set to false to turn off the policy. The policy will not be enforced even if it remains attached to a flow.

true Optional
async

This attribute is deprecated.

false Deprecated

<DisplayName> element

Use in addition to the name attribute to label the policy in the management UI proxy editor with a different, natural-language name.

<DisplayName>Policy Display Name</DisplayName>
Default

N/A

If you omit this element, the value of the policy's name attribute is used.

Presence Optional
Type String

<Source> element

The variable specifying the XML message that you want to convert to JSON.

The HTTP Content-type header of the source message must be set to application/xml, otherwise the policy is not enforced.

If <Source> is not defined, then it is treated as message, which resolves to request when the policy is attached to a request flow, or response when the policy is attached to a response flow.

If the source variable cannot be resolved, or resolves to a non-message type, the policy throws an error.

<Source>response</Source>
Default The value of the Source element
Presence Optional
Type Message

<OutputVariable> element

Specifies where to store the output of the XML to JSON format conversion. Typically, this element is not included in the policy configuration.

Apigee parses the payload of the XML message specified in Source, converts it into JSON, stores the result into the payload of OutputVariable, and sets the HTTP Content-type header of the OutputVariable message to application/json.

If OutputVariable is not specified, the value of Source is used by default. For example, if the source is response, then OutputVariable defaults to response.

<OutputVariable>response</OutputVariable>
Default the message specified in Source
Presence Optional
Type Message

<Options>

Options give you control over the conversion from XML to JSON. Use either the <Options> group, which lets you add specific conversion settings, or the <Format> element, which lets you reference a template of predefined options. You cannot use both <Options> and <Format>.

<Options> is required if <Format> isn't used.

<RecognizeNumber> element

If true, then number fields in the XML payload retain their original format.

<RecognizeNumber>true</RecognizeNumber>

Consider the following XML example:

<a>
  <b>100</b>
  <c>value</c>
</a>

If RecognizeNumber is true, the policy converts this to:

{
    "a": {
        "b": 100,
        "c": "value"
    }
}

If RecognizeNumber is false, the policy converts this to:

{
  "a": {
    "b": "100",
    "c": "value"
  }
}
Default false
Presence Optional
Type Boolean

<RecognizeBoolean> element

Lets the conversion maintain boolean true/false values rather than turning the values into strings.

<RecognizeBoolean>true</RecognizeBoolean>

For the following XML example:

<a>
  <b>true</b>
  <c>value</c>
</a>

If RecognizeBoolean is true, the policy converts this to:

{
    "a": {
        "b": true,
        "c": "value"
    }
}

If RecognizeBoolean is false, the policy converts this to:

{
    "a": {
        "b": "true",
        "c": "value"
    }
}
Default false
Presence Optional
Type Boolean

<RecognizeNull> element

Lets you convert empty values to null values.

<RecognizeNull>true</RecognizeNull>

For the following XML:

<a>
  <b></b>
  <c>value</c>
</a>

If RecognizeNull is true, and if there is no NullValue option, this XML converts to:

{
  "a": {
    "b": null,
    "c": "value"
  }
}

If RecognizeNull is false, this XML converts to:

{
  "a": {
    "b": {},
    "c": "value"
  }
}
Default false
Presence Optional
Type Boolean

<NullValue> element

Indicates the value to which recognized null values in the source message should be converted. By default the value is null. This option is effective only if RecognizeNull is true.

<NullValue>not-present</NullValue>

For the following XML:

<a>
  <b></b>
  <c>value</c>
</a>

If RecognizeNull is true, and NullValue is not specified, this XML converts to:

{
  "a": {
    "b": null,
    "c": "value"
  }
}

If RecognizeNull is true, and NullValue is not-present, this XML converts to:

{
  "a": {
    "b": "not-present",
    "c": "value"
  }
}
Default null
Presence Optional
Type String

Namespace options

By default, this policy omits XML namespaces in the generated JSON. To specify that namespaces in the XML document should be translated into the generated JSON, use these elements together.

<NamespaceBlockName>#namespaces</NamespaceBlockName>
<DefaultNamespaceNodeName>&</DefaultNamespaceNodeName>
<NamespaceSeparator>***</NamespaceSeparator>

Consider the following XML example:

<a xmlns="http://ns.com" xmlns:ns1="http://ns1.com">
  <ns1:b>value</ns1:b>
</a>

If NamespaceSeparator is not specified, the following JSON structure is generated:

{
    "a": {
        "b": "value"
    }
}

If the elements NamespaceBlockName, DefaultNamespaceNodeName, and NamespaceSeparator are specified as #namespaces, &, and ***, respectively, then the following JSON structure is generated:

{
    "a": {
        "#namespaces": {
            "&": "http://ns.com",
            "ns1": "http://ns1.com"
        },
        "ns1***b": "value"
    }
}
Default None. If <NamespaceBlockName> is not specified, the policy will generate output JSON that does not refer to any XML namespaces, regardless whether the source message referred to namespaces.
Presence Optional
However, if you specify <NamespaceBlockName>, you must also specify the other two elements.
Type Strings

Text options

Use these elements together.

      <TextAlwaysAsProperty>true|false</TextAlwaysAsProperty>
      <TextNodeName>TEXT</TextNodeName>

When the policy encounters an XML element that contains only a single text node as a child, the policy translates the text content of that XML element into a property in the JSON hash.

When the policy encounters an XML element that contains multiple children of mixed content, these options allow you to control the output JSON of any child text nodes.

For example, consider this policy configuration:

<XMLToJSON name='XMLToJSON-1'>
  <Options>
    <TextAlwaysAsProperty>???</TextAlwaysAsProperty>
    <TextNodeName>#text</TextNodeName>
  </Options>
</XMLToJSON>

For the given XML inputs, the policy will generate these outputs, depending on whether TextAlwaysAsProperty is true or false:

XML Input JSON Output
when TextAlwaysAsProperty is false when TextAlwaysAsProperty is true
<a>value1</a>
{
  "a": "value1"
}
{
  "a": {
    "#text": "value1"
  }
}
<a>value1
  <b>value2</b>
</a>
{
  "a": {
    "#text": "value1\n",
    "b": "value2"
  }
}
{
  "a": {
    "#text": "value1\n",
    "b": {
      "#text": "value2"
    }
  }
}
Default <TextAlwaysAsProperty>: false
<TextNodeName>: (blank string)
Presence Optional
Type <TextAlwaysAsProperty>: Boolean
<TextNodeName>: String

Attribute options

These elements, together, allow you to group attribute values into a JSON block and append prefixes to the attribute names.

<AttributeBlockName>FOO_BLOCK</AttributeBlockName>
<AttributePrefix>BAR_</AttributePrefix>

Consider the following XML example:

<a attrib1="value1" attrib2="value2"/>

If both the attributes (AttributeBlockName and AttributePrefix) are specified as defined in the XML to JSON example, the following JSON structure is generated:

{
  "a": {
    "FOO_BLOCK": {
      "BAR_attrib1": "value1",
      "BAR_attrib2": "value2"
    }
  }
}

If only AttributeBlockName is specified, the following JSON structure is generated:

{
    "a": {
        "FOO_BLOCK": {
            "attrib1": "value1",
            "attrib2": "value2"
        }
    }
}

If only AttributePrefix is specified, the following JSON structure is generated:

{
    "a": {
        "BAR_attrib1": "value1",
        "BAR_attrib2": "value2"
    }
}

If neither is specified, the following JSON structure is generated:

{
    "a": {
        "attrib1": "value1",
        "attrib2": "value2"
    }
}
Default none.
Presence Optional
Type String

<OutputPrefix> and <OutputSuffix> elements

Use these elements together to apply a prefix and/or suffix to the generated JSON.

<OutputPrefix>PREFIX_</OutputPrefix>
<OutputSuffix>_SUFFIX</OutputSuffix>

Consider the following XML example:

<a>value</a>

Suppose the policy configuration is as follows:

<XMLToJSON name='XMLToJSON-4'>
  <Options>
    <OutputPrefix>{ "result": </OutputPrefix>
    <OutputSuffix>}</OutputSuffix>
  </Options>
</XMLToJSON>

The following JSON structure is generated:

{
  "result": {
    "a": "value"
  }
}

You may omit either or both of OutputPrefix and OutputSuffix.

Using these elements, it is possible to generate invalid JSON. For example, using this policy configuration:

<XMLToJSON name='XMLToJSON-4'>
  <Options>
    <OutputPrefix>PREFIX_</OutputPrefix>
  </Options>
</XMLToJSON>

The policy generates the following, which is not valid JSON:

PREFIX_{
  "a" : "value"
}

If the configuration specifies neither OutputPrefix nor OutputSuffix, the policy generates the following JSON structure:

{
  "a": "value"
}
Default See samples above.
Presence Optional
Type String

<StripLevels> element

<Options>
    <StripLevels>4</StripLevels>
</Options>

Sometimes XML payloads, such as SOAP, have many parent levels you don't want to include in the converted JSON. Here's an example SOAP response containing many levels:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/Schemata-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
      <GetCityWeatherByZIPResponse xmlns="http://ws.cdyne.com/WeatherWS/">
          <GetCityWeatherByZIPResult>
              <State>CO</State>
              <City>Denver</City>
              <Description>Sunny</Description>
              <Temperature>62</Temperature>
          </GetCityWeatherByZIPResult>
      </GetCityWeatherByZIPResponse>
  </soap:Body>
</soap:Envelope>

There are 4 levels before you get to the State, City, Description, and Temperature level. Without using <StripLevels>, the converted JSON response would look like this:

{
   "Envelope" : {
      "Body" : {
         "GetCityWeatherByZIPResponse" : {
            "GetCityWeatherByZIPResult" : {
               "State" : "CO",
               "City" : "Denver",
               "Description" : "Sunny",
               "Temperature" : "62"
            }
         }
      }
   }
}

If you want to strip those first 4 levels in the JSON response, you'd set <StripLevels>4</StripLevels>, which would give you the following JSON:

{
  "State" : "CO",
  "City" : "Denver",
  "Description" : "Sunny",
  "Temperature" : "62"
}

You can strip levels away up to the first element that contains multiple children. What does that mean? Let's look at a more complex JSON example:

{
   "Envelope" : {
      "Body" : {
         "GetCityForecastByZIPResponse" : {
            "GetCityForecastByZIPResult" : {
               "ResponseText" : "City Found",
               "ForecastResult" : {
                  "Forecast" : [
                     {
                        "ProbabilityOfPrecipiation" : {
                           "Nighttime" : "00",
                           "Daytime" : 10
                        }  ...

Level 3 in this example is GetCityForecastByZIPResponse, which has only one child. So if you were to use <StripLevels>3</StripLevels> (remove the first three levels), the JSON would look like this:

{
   "GetCityForecastByZIPResult" : {
      "ResponseText" : "City Found",
      "ForecastResult" : {
         "Forecast" : [
            {
               "ProbabilityOfPrecipiation" : {
                  "Nighttime" : "00",
                  "Daytime" : 10
               }  ...

Notice that GetCityForecastByZIPResult has multiple children. Since it's the first element containing multiple children, you can strip this last level using <StripLevels>4</StripLevels>, which will give you the following JSON:

{
   "ResponseText" : "City Found",
   "ForecastResult" : {
      "Forecast" : [
         {
            "ProbabilityOfPrecipiation" : {
               "Nighttime" : "00",
               "Daytime" : 10
            }  ...

Because level 4 is the first level containing multiple children, you can't strip any levels lower than this. If you set the strip level to 5, 6, 7, and so on, you'll continue to get the response above.

Default 0 (no level stripping)
Presence Optional
Type Integer

<TreatAsArray>/<Path> element

<Options>
    <TreatAsArray>
        <Path unwrap="true">teachers/teacher/studentnames/name</Path>
    </TreatAsArray>
</Options>

This element combination lets you ensure that values from an XML document are always translated to a JSON array. This can be useful when the number of child elements varies for different payloads. Apigee's default behavior is to convert multiple child elements of the same name to a JSON array and single child elements to a JSON primitive. The TreatAsArray option lets you ensure that child elements are always translated to a JSON array.

Using TreatAsArray can improve the structure of subsequent code that handles the output, because data from the array is returned the same way every time. For example, evaluating a JSONPath of $.teachers.teacher.studentnames.name[0] will return the first name value consistently, whether the original XML contained one, or more than one, name element.

Let's take a step back and look at the XML to JSON default behavior, then explore how to control the output using <TreatAsArray>/<Path>.

When an XML document contains an element with multiple occurrences (which can happen when the XML schema specifies maxOccurs='unbounded' for the element), the XML to JSON policy automatically puts those values in an array. For example, the following XML block

<teacher>
    <name>teacherA</name>
    <studentnames>
        <name>student1</name>
        <name>student2</name>
    </studentnames>
</teacher>

...gets converted into the following JSON automatically with no special policy configuration:

{
  "teachers" : {
    "teacher" : {
      "name" : "teacherA",
      "studentnames" : {
        "name" : [
          "student1",
          "student2"
        ]
      }
    }
  }
}

Notice that the two student names are contained in an array.

However, if only one student appears in the XML document, the XML to JSON policy automatically treats the value as a single string, not an array of strings, as shown in the following example:

{
  "teachers" : {
    "teacher" : {
      "name" : "teacherA",
      "studentnames" : {
        "name" : "student1"
      }
    }
  }
}

In the previous examples, similar data was converted differently, once to an array, another to a single string. The <TreatAsArray>/<Path> element lets you control the output to make sure that the student names are always converted into an array even if there's only one value. You configure this by identifying the Path to the element whose values you want to convert into an array, like so:

<Options>
    <TreatAsArray>
        <Path>teachers/teacher/studentnames/name</Path>
    </TreatAsArray>
</Options>

The configuration above would generate the JSON like this:

{
  "teachers" : {
    "teacher" : {
      "name" : "teacherA",
      "studentnames" : {
        "name" : ["student1"]
      }
    }
  }
}

Notice that student1 is now in an array. Now, regardless of whether there are one or multiple students, you can retrieve them from a JSON array in your code using the following JSONPath: $.teachers.teacher.studentnames.name[0]

The <Path> element also has an unwrap attribute, explained in the next section.

Default N/A
Presence Optional
Type String

Attributes

 <Options>
    <TreatAsArray>
        <Path unwrap="true">teachers/teacher/studentnames/name</Path>
    </TreatAsArray>
</Options>
Attribute Description Presence Type
unwrap

Default: false

Removes the element from the JSON output. Use this to streamline or flatten ("unwrap") the JSON, which also shortens the JSONPath needed to retrieve values. For example, instead of $.teachers.teacher.studentnames.name[*], you could flatten the JSON and use $.teachers.studentnames[*].

Here's a JSON example:

{
  "teachers" : {
      "teacher" : {
          "name" : "teacherA",
          "studentnames" : {
              "name" : [
                 "student1",
                 "student2"
              ]}...

In this example, you could argue that the teacher element and the studentnames name element are unnecessary. So you can remove, or unwrap them. Here's how you'd configure the <Path> element to do this:

<TreatAsArray>
    <Path unwrap="true">teachers/teacher</Path>
    <Path unwrap="true">teachers/teacher/studentnames/name</Path>
</TreatAsArray>

The unwrap attribute is set to true, and the paths to the elements to unwrap are provided. The JSON output will now look like this:

{
  "teachers" : [{
      "name" : "teacherA",
      "studentnames" : ["student1","student2"]
      }]...

Note that because the <Path> element is in the <TreatAsArray> element, both of the elements in the Path will be treated as arrays in the JSON output.

Optional Boolean

For more examples and a feature walkthrough, see this Google Cloud Community article..

<Format>

Format gives you control over the conversion from XML to JSON. Enter the name of a predefined template that contains a specific combination of Options elements described in this topic. Predefined formats include: xml.com, yahoo, google, badgerFish.

Use either the <Format> element or the <Options> group. You cannot use both <Format> and <Options>.

Following are the Format definitions of each predefined template.

xml.com

<RecognizeNull>true</RecognizeNull>
<TextNodeName>#text</TextNodeName>
<AttributePrefix>@</AttributePrefix>

yahoo

<RecognizeNumber>true</RecognizeNumber>
<TextNodeName>content</TextNodeName>

google

<TextNodeName>$t</TextNodeName>
<NamespaceSeparator>$</NamespaceSeparator>
<TextAlwaysAsProperty>true</TextAlwaysAsProperty>

badgerFish

<TextNodeName>$</TextNodeName>
<TextAlwaysAsProperty>true</TextAlwaysAsProperty>
<AttributePrefix>@</AttributePrefix>
<NamespaceSeparator>:</NamespaceSeparator>
<NamespaceBlockName>@xmlns</NamespaceBlockName>
<DefaultNamespaceNodeName>$</DefaultNamespaceNodeName>

Element syntax:

<Format>yahoo</Format>
Default (none)
Valid values One of the following:
xml.com, yahoo, google, badgerFish
Presence Optional, but Required if <Options> isn't used.
Type String

Schemas


Error reference

本部分介绍当此政策触发错误时返回的故障代码和错误消息,以及由 Apigee 设置的故障变量。在开发故障规则以处理故障时,请务必了解此信息。如需了解详情,请参阅您需要了解的有关政策错误的信息和处理故障。

运行时错误

政策执行时可能会发生这些错误。

故障代码 HTTP 状态 原因 修复
steps.xmltojson.ExecutionFailed ExecutionFailed 当输入负载 (XML) 为空或输入 XML 无效或格式有误时,就会出现此错误。
steps.xmltojson.InCompatibleTypes ExecutionFailed 如果 <Source> 元素中定义的变量类型与 <OutputVariable> 元素不同,则会出现此错误。<Source> 元素中包含的变量类型和 <OutputVariable> 元素必须与此变量完全匹配。
steps.xmltojson.InvalidSourceType ExecutionFailed 如果用于定义 <Source> 元素的变量类型无效,则会出现此错误。变量的有效类型为消息和字符串。
steps.xmltojson.OutputVariableIsNotAvailable ExecutionFailed 如果 XML 到 JSON 政策 的 <Source> 元素中指定的变量为字符串类型,并且未定义 <OutputVariable> 元素,则会出现此错误。如果 <Source> 元素中定义的变量为字符串类型,则 <OutputVariable> 元素是必填的。
steps.xmltojson.SourceUnavailable ExecutionFailed 如果 XML 到 JSON 政策的 <Source> 元素中的 message 变量是以下任一项,便会出现此错误:
  • 超出范围(在执行政策的特定流中不可用)
  • 无法解析(未定义)

部署错误

在您部署包含此政策的代理时,可能会发生这些错误。

错误名称 原因 修复
EitherOptionOrFormat 如果 XML 到 JSON 政策中未声明 <Options> 或 <Format> 中的一个元素,则 API 代理的部署将失败。
UnknownFormat 如果 XML 到 JSON 政策中的 <Format> 元素定义了未知格式,则 API 代理的部署将失败。预定义格式包括:xml.com、yahoo、google 和 badgerFish。

故障变量

发生运行时错误时,系统会设置这些变量。如需了解详情,请参阅您需要了解的有关政策错误的信息。

变量 其中 示例
fault.name="fault_name" fault_name 是故障名称,如上面的运行时错误表中所列。故障名称是故障代码的最后一部分。 fault.name = "SourceUnavailable"
xmltojson.policy_name.failed policy_name 是抛出故障的政策的用户指定名称。 xmltojson.XMLtoJSON-1.failed = true

错误响应示例

{
  "fault": {
    "faultstring": "XMLToJSON[XMLtoJSON-1]: Source xyz is not available",
    "detail": {
      "errorcode": "steps.xml2json.SourceUnavailable"
    }
  }
}

故障规则示例

<faultrule name="VariableOfNonMsgType"></faultrule><FaultRule name="XML to JSON Faults">
    <Step>
        <Name>AM-SourceUnavailableMessage</Name>
        <Condition>(fault.name Matches "SourceUnavailable") </Condition>
    </Step>
    <Step>
        <Name>AM-BadXML</Name>
        <Condition>(fault.name = "ExecutionFailed")</Condition>
    </Step>
    <Condition>(xmltojson.XMLtoJSON-1.failed = true) </Condition>
</FaultRule>

Related topics

JSON to XML: JSONtoXML policy