Introduction
This article is about the "JSON undefined" error in InfoPath 2013 forms when the PeopePicker is used, and explains how the error can be fixed. The solution is presented first. Secondly, it explains when and why the error occurs, and explains why many solutions do not work on Stackoverflow, Codeproject, or Google Search.
Solution
Douglas Crockford has provided the json2.js script on GitHub. Make a copy of this script and paste it into the LAYOUTS directory in SharePoint 2013. Typically, you create a directory scripts before and paste json2.js into that directory. Locate the file pickerdialog.master in the LAYOUTS directory and make a copy of this file for backup. Open the file pickerdialog.master and find the following line
<sharepoint:scriptlink language="javascript" name="core.js" localizable="false" runat="server" />
Above this line, create this entry
<sharepoint:scriptlink language="javascript" name="/_layouts/15/Scripts/json2.js" localizable="false" runat="server" />
Save the modification in the file pickerdialog.master.
Now you can open an InfoPath 2013 form which contains a PeoplePicker Control in Internet Explorer 11. Make sure that the compatibility mode is disabled in Internet Explorer 11 for the SharePoint 2013 domain. Try to select a user with the PeoplePicker Control. The "JSON undefined" error is no longer displayed.
Why this solution
The advantage of this solution is that it can work with any user without making settings in compatibility mode in Internet Explorer 11. Changing the compatibility mode is stupid because the behavior of the PeoplePicker dialog in an InfoPath form is not changed. In many solutions on Stackoverflow or CodeProject and on many other sites this is explained. This can work for one user. For many users, you need to tell them how to change the compatibility mode. Imagine your company has 1000 users. This solution is not practical.
Another advantage, json2 checks if the native JSON is available in the browser. It only works when it does not find the native JSON.
Another very common solution is to change the META tags X-UA-Compatible
in the file pickerdialog.master from
<meta http-equiv="X-UA-Compatible" content="IE=IE10">
to
<meta http-equiv="X-UA-Compatible" content="IE=IE11">
or
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE10">
But that's nonsense, too. The PeoplePicker dialog is loaded in the context of the InfoPath form. The InfoPath form defines the compatibility mode outgoing from the browser's browser version and renderengine.
Browser | Major Version | Render Engine | X-UA-Compatible |
---|
IE 11 (Without Compatibility Mode) | 11 | Trident/7.0 | IE11 |
IE11 (With Compatibility Mode) | 7 | Trident/7.0 | IE8 |
IE 10 (Without Compatibility Mode) | 11 | Trident/7.0 | IE10 |
IE 10 (With compatibility mode) | 7 | Trident/7.0 | IE8 |
There is no way to tell SharePoint to load InfoPath with X-UA-Compatible IE10 or IE11. There is no META tag in the FormServer.aspx file and no server control to set the compatibility mode. If the InfoPath form is loaded in compatibility mode IE8, the JavaScript engine of the IE8 is also loaded. In IE 8 the native JSON was implemented for parsing and serializing. The "JSON undefined" error occurs in the SharePoint Server script resources. The problem is that the native JSON is not present in the IE8 compatibility mode due to a faulty implementation.
Where is X-UA-Compatible IE8 set in InfoPath?
To clarify this question you have to look at the control
InfoPath:XmlFormView
The control can be found in the assembly
Microsoft.Office.InfoPath.Server, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
The assembly is located in the Global Assembly Cache (GAC).
With the tool ILSpy you can see how the code for writing the META tag X-UA-Compatible is implemented in the assembly.
The Control XmlFormView
is located in the Namespace Microsoft.Office.InfoPath.Server.Controls
. A private method onDataBindHelper()
is implemented in the control. There are two relevant lines in this method
Browser.DetectBrowserAndVersion(HttpContext.Current, sPWeb2.UIVersion, out browser2, out browserVersion);
this.AddIEMetaTag(browserVersion);
The method DetectBrowserAndVersion
determines the browser and the major version.
internal static void DetectBrowserAndVersion(HttpContext context, int uiVersion, out Browser browser, out int majorVersion)
{
bool flag = uiVersion >= 4;
BrowserType key;
if (string.IsNullOrEmpty(context.Request.UserAgent))
{
key = BrowserType.None;
majorVersion = -1;
}
else
{
majorVersion = context.Request.Browser.MajorVersion;
if (context.Request.UserAgent.Contains(Browser.BrowserNames[2]))
{
key = BrowserType.Safari;
}
else if (string.Compare(context.Request.Browser.Browser, Browser.BrowserNames[1], StringComparison.OrdinalIgnoreCase) == 0 || context.Request.UserAgent.Contains("Trident"))
{
if (flag)
{
if (context.Request.UserAgent.Contains("Trident/6.0"))
{
key = BrowserType.IE;
majorVersion = 10;
}
else if (context.Request.UserAgent.Contains("Trident/5.0"))
{
key = BrowserType.IE;
majorVersion = 9;
}
else if (majorVersion > 7)
{
key = BrowserType.IE;
}
else if (majorVersion == 7 && context.Request.UserAgent.Contains("Trident"))
{
key = BrowserType.IE;
majorVersion = 8;
}
else
{
key = BrowserType.IE7Strict;
}
}
else
{
key = BrowserType.IE;
}
}
else if (string.Compare(context.Request.Browser.Browser, Browser.BrowserNames[3], StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(context.Request.Browser.Browser, "Mozilla", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(context.Request.Browser.Browser, "FireFox", StringComparison.OrdinalIgnoreCase) == 0 || context.Request.UserAgent.Contains("Mozilla"))
{
key = BrowserType.Netscape;
}
else
{
key = BrowserType.None;
}
}
browser = Browser.Browsers[key];
}
This code works perfectly. The major version and the used browser are read from the request context. The BrowserNames
listing is implemented as
private static readonly string[] BrowserNames = new string[]
{
"None",
"IE",
"Safari",
"Netscape",
"Mobile",
"IE7Strict"
};
The method AddIEMetaTag
adds the META tag X-UA-Compatible to the InfoPath form
private void AddIEMetaTag(int browserVersion)
{
HtmlHead header = this.Page.Header;
if (header != null && this._controlStage == XmlFormView.ControlStage.BeforeRender)
{
SPSite sPSite = SiteAndWebCache.Fetch().EnsureRequestSite();
StringBuilder stringBuilder = new StringBuilder();
HtmlMeta htmlMeta = new HtmlMeta();
htmlMeta.HttpEquiv = "X-UA-Compatible";
stringBuilder.Append("IE=");
if (sPSite.CompatibilityLevel < 15)
{
stringBuilder.Append("8");
}
else
{
stringBuilder.Append(browserVersion);
}
htmlMeta.Content = stringBuilder.ToString();
header.Controls.AddAt(0, htmlMeta);
}
}
You can see that the compatibility mode is set by the browser version if the site's CompatibilityLevel is equal to or greater than 15. If the site's CompatibilityLevel is less than 15, the compatibility mode is set to IE8.
In my opinion there is a problem with this code
The InfoPath form is often loaded with compatibility mode IE8, although Internet Explorer 11 is used. On the basis of the code, you indicate that the browser version is passed to the META Tag X-UA-Compatible correctly. Theoretically! But what if the CompatibilityLevel property of the sPSite object is less than 15? The CompatibilityLevel property gets its value from the major part of the SchemaVersion of the sPSite object. The SPSite object is stored in an InfoPath internal cache. Possibly, when reading from this internal cache, the object loses some information because the required instances are not loaded. And therefore the compatibility mode IE8 could be generated.
Conclusion
The solution provided by me should work for all others. It is not perfect because a SharePoint's own system file is changed by hand. For the next SharePoint update, it may be necessary to replace the pickerdialog.master file with a new version. As long as this does not happen, this solution works.
In a SharePoint farm with 30 site collections and more than 100 InfoPath forms, there are no problems with the PeoplePicker since the implementation of json2.js.