Send Docs Feedback

XML to JSON policy

What

This 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.

Where

This policy can be attached in the following locations.

ProxyEndpoint TargetEndpoint
    PreFlow Flow PostFlow PreFlow Flow PostFlow    
Request    
    Response
    PostFlow Flow PreFlow PostFlow Flow PreFlow    

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).

About

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.


Samples

For a detailed discussion on converting between JSON and XML, see http://community.apigee.com/articles/1839/converting-between-xml-and-json-what-you-need-to-k.html.

<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. Edge 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 attributes are common to all policy parent elements.

Attribute Description Default Presence
name

The internal name of the policy. Characters you can use in the name are restricted to: A-Z0-9._\-$ %. However, the Edge management UI enforces additional restrictions, such as automatically removing characters that are not alphanumeric.

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.

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 the value of the policy's name attribute is used.

Presence: Optional
Type: String

 

<Source> element

The variable, request or response, that contains 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 request or response, determined by where the policy is added to the API proxy flow
Presence Optional
Type message

<OutputVariable> element

Stores the output of the XML to JSON format conversion. This is usually the same value as the source, that is, usually XML response is converted to a JSON response.

The payload of the XML message is parsed and converted into JSON, and the HTTP Content-type header of the XML-formatted message is set to application/json.

If OutputVariable is not specified, the source is treated as OutputVariable. For example, if the source is response, then OutputVariable defaults to response.

<OutputVariable>response</Response>
Default request or response, determined by where the policy is added to the API proxy flow
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.

<Options>/<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 true, converts to:

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

If false, converts to:

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

<Options>/<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 true, converts to:

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

If false, converts to:

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

<Options>/<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 true, converts to:

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

If false, converts to:

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

<Options>/<NullValue> element

Indicates what constitutes a null value in the message to be converted. By default the value is NULL.

<NullValue>NULL</NullValue>
Default NULL
Presence Optional
Type String

<Options>/<NamespaceBlockName>
<Options>/<DefaultNamespaceNodeName>
<Options>/<NamespaceSeparator> elements

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 See examples above.
Presence Optional
However, if you specify <NamespaceBlockName>, you must also specify the other two elements.
Type Strings

<Options>/<TextAlwaysAsProperty>
<Options>/<TextNodeName> elements

Use these elements together.

If set to true, the content of the XML element is converted to a string property.

<TextAlwaysAsProperty>true</TextAlwaysAsProperty>
<TextNodeName>TEXT</TextNodeName>

For the following XML:

<a>
  <b>value1</b>
  <c>value2<d>value3</d>value4</c>
</a>

If TextAlwaysAsProperty is set to true and TextNodeName specified as TEXT, the following JSON structure is generated:

{
  "a": {
    "b": {
      "TEXT": "value1"
    },
    "c": {
      "TEXT": [
        "value2",
        "value4"
        ],
        "d": {
          "TEXT": "value3"
        }
      }
    }
}

If TextAlwaysAsProperty is set to false and TextNodeName specified as TEXT, the following JSON structure is generated:

{
  "a": {
    "b": "value1",
    "c": {
      "TEXT": [
        "value2",
        "value4"
      ],
      {
        "d": "value3",
      }
    }
}
Default <TextAlwaysAsProperty>: false
<TextNodeName>: N/A
Presence Optional
Type <TextAlwaysAsProperty>: Boolean
<TextNodeName>: String

<Options>/<AttributeBlockName>
<Options>/<AttributePrefix> elements

Use these elements together.

Lets you group 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 See examples above.
Presence Optional
Type String

<Options>/<OutputPrefix>
<Options>/<OutputSuffix> elements

Use these elements together.

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

Consider the following XML example:

<a>value</a>

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

PREFIX_{
    "a": "value"
}_SUFFIX

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

PREFIX_{
  "a" : "value"
}

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

{
  "a" : "value"
}_SUFFIX

If neither OutputPrefix nor OutputSuffix is specified, the following JSON structure is generated:

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

<Options>/<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

<Options>/<TreatAsArray>/<Path> element

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

This element combination lets you automatically write values from an XML document into an array in JSON. This is useful, for example, when the number of child elements can vary (from one to multiple), and you want to ensure the values are always in an array. Doing this helps keep your code stable, because you can get data from the array the same way every time, for example $.teachers.teacher.studentnames[0] regardless of whether there are one or multiple values.

Let's 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 child values (usually based on a schema where the element's maxOccurs='unbounded'), 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 put into 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 as an array, another as a single string. This is where the <TreatAsArray>/<Path> element lets you control the output. You can, for example, make sure that the student names are always put in an array even if there's only one value. You configure this by identifying the Path to the element whose values you want to put in an array, like so:

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

The Path is not JSONPath. It's simply the hierarchical path to the element whose values you want to put in an array.

The configuration above would write 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 NA
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

<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>

If you're an Apigee Edge for Private Cloud customer, you can add your own Formats to the following file on each message processor: /opt/apigee4/conf/apigee/message-processor/xmltojson-formats.xml. Restart your message processors after modifying the file.

Element syntax:

<Format>yahoo</Format>
Default Enter the name of an available format:
xml.com, yahoo, google, badgerFish
Presence Required if <Options> isn't used.
Type String

Schemas

See our GitHub repository samples for the most recent schemas.


Error codes

Errors returned from Edge policies follow a consistent format as described in the Error code reference.

The XML to JSON Policy type defines the following error codes:

Error Code Message
FormatUnavailable XMLToJSON[{0}]: Format {1} is not available
SourceUnavailable XMLToJSON[{0}]: Source {1} is not available
ExecutionFailed XMLToJSON[{0}]: Execution failed. reason: {1}
InvalidSourceType JSONToXML[{0}]: Invalid source type {0}. Valid source types are {1}.
InCompatibleTypes JSONToXML[{0}]: String can not be assigned to message type.
OutputVariableIsNotAvailable JSONToXML[{0}]: Output variable is not available.

Related topics

For working samples of API proxies, see the Samples reference.

JSON to XML: JSON to XML policy

Help or comments?