We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Schema URL is null for some messages and "" for others
While working on the getting started guide, I notice the console exporter will sometimes output empty string and sometimes `null` for schema URL:

```json
{
  "resource_metrics": [
    {
      "resource": {
        "attributes": {
          "telemetry.sdk.language": "python",
          "telemetry.sdk.name": "opentelemetry",
          "telemetry.sdk.version": "1.12.0rc1",
          "telemetry.auto.version": "0.31b0",
          "service.name": "unknown_service"
        },
        "schema_url": ""
      },
      "scope_metrics": [
        {
          "scope": {
            "name": "app",
            "version": "",
            "schema_url": null
          },
          "metrics": [
            {
              "name": "roll_counter",
              "description": "The number of rolls by roll value",
              "unit": "",
              "data": {
                "data_points": [
                  {
                    "attributes": {
                      "roll.value": 4
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 3
                  },
                  {
                    "attributes": {
                      "roll.value": 6
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 4
                  },
                  {
                    "attributes": {
                      "roll.value": 5
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 1
                  },
                  {
                    "attributes": {
                      "roll.value": 1
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 2
                  },
                  {
                    "attributes": {
                      "roll.value": 3
                    },
                    "start_time_unix_nano": 1654790325350232600,
                    "time_unix_nano": 1654790332211598800,
                    "value": 1
                  }
                ],
                "aggregation_temporality": 2,
                "is_monotonic": true
              }
            }
          ],
          "schema_url": null
        }
      ],
      "schema_url": ""
    }
  ]
}
```

Note, in OTLP protobuf these two are equivalent. I think we should have consistent treatment of the empty value for these in the SDK though.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from json import dumps
15 from typing import Optional
16 
17 from deprecated import deprecated
18 
19 
20 class InstrumentationInfo:
21     """Immutable information about an instrumentation library module.
22 
23     See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these
24     properties.
25     """
26 
27     __slots__ = ("_name", "_version", "_schema_url")
28 
29     @deprecated(version="1.11.1", reason="You should use InstrumentationScope")
30     def __init__(
31         self,
32         name: str,
33         version: Optional[str] = None,
34         schema_url: Optional[str] = None,
35     ):
36         self._name = name
37         self._version = version
38         self._schema_url = schema_url
39 
40     def __repr__(self):
41         return f"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})"
42 
43     def __hash__(self):
44         return hash((self._name, self._version, self._schema_url))
45 
46     def __eq__(self, value):
47         return type(value) is type(self) and (
48             self._name,
49             self._version,
50             self._schema_url,
51         ) == (value._name, value._version, value._schema_url)
52 
53     def __lt__(self, value):
54         if type(value) is not type(self):
55             return NotImplemented
56         return (self._name, self._version, self._schema_url) < (
57             value._name,
58             value._version,
59             value._schema_url,
60         )
61 
62     @property
63     def schema_url(self) -> Optional[str]:
64         return self._schema_url
65 
66     @property
67     def version(self) -> Optional[str]:
68         return self._version
69 
70     @property
71     def name(self) -> str:
72         return self._name
73 
74 
75 class InstrumentationScope:
76     """A logical unit of the application code with which the emitted telemetry can be
77     associated.
78 
79     See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these
80     properties.
81     """
82 
83     __slots__ = ("_name", "_version", "_schema_url")
84 
85     def __init__(
86         self,
87         name: str,
88         version: Optional[str] = None,
89         schema_url: Optional[str] = None,
90     ) -> None:
91         self._name = name
92         self._version = version
93         self._schema_url = schema_url
94 
95     def __repr__(self) -> str:
96         return f"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})"
97 
98     def __hash__(self) -> int:
99         return hash((self._name, self._version, self._schema_url))
100 
101     def __eq__(self, value: object) -> bool:
102         if not isinstance(value, InstrumentationScope):
103             return NotImplemented
104         return (self._name, self._version, self._schema_url) == (
105             value._name,
106             value._version,
107             value._schema_url,
108         )
109 
110     def __lt__(self, value: object) -> bool:
111         if not isinstance(value, InstrumentationScope):
112             return NotImplemented
113         return (self._name, self._version, self._schema_url) < (
114             value._name,
115             value._version,
116             value._schema_url,
117         )
118 
119     @property
120     def schema_url(self) -> Optional[str]:
121         return self._schema_url
122 
123     @property
124     def version(self) -> Optional[str]:
125         return self._version
126 
127     @property
128     def name(self) -> str:
129         return self._name
130 
131     def to_json(self, indent=4) -> str:
132         return dumps(
133             {
134                 "name": self._name,
135                 "version": self._version,
136                 "schema_url": self._schema_url,
137             },
138             indent=indent,
139         )
140
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py
@@ -35,6 +35,8 @@
     ):
         self._name = name
         self._version = version
+        if schema_url is None:
+            schema_url = ""
         self._schema_url = schema_url
 
     def __repr__(self):
@@ -90,6 +92,8 @@
     ) -> None:
         self._name = name
         self._version = version
+        if schema_url is None:
+            schema_url = ""
         self._schema_url = schema_url
 
     def __repr__(self) -> str:

{"golden_diff": "diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py\n--- a/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py\n+++ b/opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py\n@@ -35,6 +35,8 @@\n     ):\n         self._name = name\n         self._version = version\n+        if schema_url is None:\n+            schema_url = \"\"\n         self._schema_url = schema_url\n \n     def __repr__(self):\n@@ -90,6 +92,8 @@\n     ) -> None:\n         self._name = name\n         self._version = version\n+        if schema_url is None:\n+            schema_url = \"\"\n         self._schema_url = schema_url\n \n     def __repr__(self) -> str:\n", "issue": "Schema URL is null for some messages and \"\" for others\nWhile working on the getting started guide, I notice the console exporter will sometimes output empty string and sometimes `null` for schema URL:\r\n\r\n```json\r\n{\r\n  \"resource_metrics\": [\r\n    {\r\n      \"resource\": {\r\n        \"attributes\": {\r\n          \"telemetry.sdk.language\": \"python\",\r\n          \"telemetry.sdk.name\": \"opentelemetry\",\r\n          \"telemetry.sdk.version\": \"1.12.0rc1\",\r\n          \"telemetry.auto.version\": \"0.31b0\",\r\n          \"service.name\": \"unknown_service\"\r\n        },\r\n        \"schema_url\": \"\"\r\n      },\r\n      \"scope_metrics\": [\r\n        {\r\n          \"scope\": {\r\n            \"name\": \"app\",\r\n            \"version\": \"\",\r\n            \"schema_url\": null\r\n          },\r\n          \"metrics\": [\r\n            {\r\n              \"name\": \"roll_counter\",\r\n              \"description\": \"The number of rolls by roll value\",\r\n              \"unit\": \"\",\r\n              \"data\": {\r\n                \"data_points\": [\r\n                  {\r\n                    \"attributes\": {\r\n                      \"roll.value\": 4\r\n                    },\r\n                    \"start_time_unix_nano\": 1654790325350232600,\r\n                    \"time_unix_nano\": 1654790332211598800,\r\n                    \"value\": 3\r\n                  },\r\n                  {\r\n                    \"attributes\": {\r\n                      \"roll.value\": 6\r\n                    },\r\n                    \"start_time_unix_nano\": 1654790325350232600,\r\n                    \"time_unix_nano\": 1654790332211598800,\r\n                    \"value\": 4\r\n                  },\r\n                  {\r\n                    \"attributes\": {\r\n                      \"roll.value\": 5\r\n                    },\r\n                    \"start_time_unix_nano\": 1654790325350232600,\r\n                    \"time_unix_nano\": 1654790332211598800,\r\n                    \"value\": 1\r\n                  },\r\n                  {\r\n                    \"attributes\": {\r\n                      \"roll.value\": 1\r\n                    },\r\n                    \"start_time_unix_nano\": 1654790325350232600,\r\n                    \"time_unix_nano\": 1654790332211598800,\r\n                    \"value\": 2\r\n                  },\r\n                  {\r\n                    \"attributes\": {\r\n                      \"roll.value\": 3\r\n                    },\r\n                    \"start_time_unix_nano\": 1654790325350232600,\r\n                    \"time_unix_nano\": 1654790332211598800,\r\n                    \"value\": 1\r\n                  }\r\n                ],\r\n                \"aggregation_temporality\": 2,\r\n                \"is_monotonic\": true\r\n              }\r\n            }\r\n          ],\r\n          \"schema_url\": null\r\n        }\r\n      ],\r\n      \"schema_url\": \"\"\r\n    }\r\n  ]\r\n}\r\n```\r\n\r\nNote, in OTLP protobuf these two are equivalent. I think we should have consistent treatment of the empty value for these in the SDK though.\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom json import dumps\nfrom typing import Optional\n\nfrom deprecated import deprecated\n\n\nclass InstrumentationInfo:\n    \"\"\"Immutable information about an instrumentation library module.\n\n    See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these\n    properties.\n    \"\"\"\n\n    __slots__ = (\"_name\", \"_version\", \"_schema_url\")\n\n    @deprecated(version=\"1.11.1\", reason=\"You should use InstrumentationScope\")\n    def __init__(\n        self,\n        name: str,\n        version: Optional[str] = None,\n        schema_url: Optional[str] = None,\n    ):\n        self._name = name\n        self._version = version\n        self._schema_url = schema_url\n\n    def __repr__(self):\n        return f\"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})\"\n\n    def __hash__(self):\n        return hash((self._name, self._version, self._schema_url))\n\n    def __eq__(self, value):\n        return type(value) is type(self) and (\n            self._name,\n            self._version,\n            self._schema_url,\n        ) == (value._name, value._version, value._schema_url)\n\n    def __lt__(self, value):\n        if type(value) is not type(self):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) < (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    @property\n    def schema_url(self) -> Optional[str]:\n        return self._schema_url\n\n    @property\n    def version(self) -> Optional[str]:\n        return self._version\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n\nclass InstrumentationScope:\n    \"\"\"A logical unit of the application code with which the emitted telemetry can be\n    associated.\n\n    See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these\n    properties.\n    \"\"\"\n\n    __slots__ = (\"_name\", \"_version\", \"_schema_url\")\n\n    def __init__(\n        self,\n        name: str,\n        version: Optional[str] = None,\n        schema_url: Optional[str] = None,\n    ) -> None:\n        self._name = name\n        self._version = version\n        self._schema_url = schema_url\n\n    def __repr__(self) -> str:\n        return f\"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})\"\n\n    def __hash__(self) -> int:\n        return hash((self._name, self._version, self._schema_url))\n\n    def __eq__(self, value: object) -> bool:\n        if not isinstance(value, InstrumentationScope):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) == (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    def __lt__(self, value: object) -> bool:\n        if not isinstance(value, InstrumentationScope):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) < (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    @property\n    def schema_url(self) -> Optional[str]:\n        return self._schema_url\n\n    @property\n    def version(self) -> Optional[str]:\n        return self._version\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    def to_json(self, indent=4) -> str:\n        return dumps(\n            {\n                \"name\": self._name,\n                \"version\": self._version,\n                \"schema_url\": self._schema_url,\n            },\n            indent=indent,\n        )\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom json import dumps\nfrom typing import Optional\n\nfrom deprecated import deprecated\n\n\nclass InstrumentationInfo:\n    \"\"\"Immutable information about an instrumentation library module.\n\n    See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these\n    properties.\n    \"\"\"\n\n    __slots__ = (\"_name\", \"_version\", \"_schema_url\")\n\n    @deprecated(version=\"1.11.1\", reason=\"You should use InstrumentationScope\")\n    def __init__(\n        self,\n        name: str,\n        version: Optional[str] = None,\n        schema_url: Optional[str] = None,\n    ):\n        self._name = name\n        self._version = version\n        if schema_url is None:\n            schema_url = \"\"\n        self._schema_url = schema_url\n\n    def __repr__(self):\n        return f\"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})\"\n\n    def __hash__(self):\n        return hash((self._name, self._version, self._schema_url))\n\n    def __eq__(self, value):\n        return type(value) is type(self) and (\n            self._name,\n            self._version,\n            self._schema_url,\n        ) == (value._name, value._version, value._schema_url)\n\n    def __lt__(self, value):\n        if type(value) is not type(self):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) < (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    @property\n    def schema_url(self) -> Optional[str]:\n        return self._schema_url\n\n    @property\n    def version(self) -> Optional[str]:\n        return self._version\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n\nclass InstrumentationScope:\n    \"\"\"A logical unit of the application code with which the emitted telemetry can be\n    associated.\n\n    See `opentelemetry.trace.TracerProvider.get_tracer` for the meaning of these\n    properties.\n    \"\"\"\n\n    __slots__ = (\"_name\", \"_version\", \"_schema_url\")\n\n    def __init__(\n        self,\n        name: str,\n        version: Optional[str] = None,\n        schema_url: Optional[str] = None,\n    ) -> None:\n        self._name = name\n        self._version = version\n        if schema_url is None:\n            schema_url = \"\"\n        self._schema_url = schema_url\n\n    def __repr__(self) -> str:\n        return f\"{type(self).__name__}({self._name}, {self._version}, {self._schema_url})\"\n\n    def __hash__(self) -> int:\n        return hash((self._name, self._version, self._schema_url))\n\n    def __eq__(self, value: object) -> bool:\n        if not isinstance(value, InstrumentationScope):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) == (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    def __lt__(self, value: object) -> bool:\n        if not isinstance(value, InstrumentationScope):\n            return NotImplemented\n        return (self._name, self._version, self._schema_url) < (\n            value._name,\n            value._version,\n            value._schema_url,\n        )\n\n    @property\n    def schema_url(self) -> Optional[str]:\n        return self._schema_url\n\n    @property\n    def version(self) -> Optional[str]:\n        return self._version\n\n    @property\n    def name(self) -> str:\n        return self._name\n\n    def to_json(self, indent=4) -> str:\n        return dumps(\n            {\n                \"name\": self._name,\n                \"version\": self._version,\n                \"schema_url\": self._schema_url,\n            },\n            indent=indent,\n        )\n", "path": "opentelemetry-sdk/src/opentelemetry/sdk/util/instrumentation.py"}]}