|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Google.Protobuf;
using OpenTelemetry.Proto.Collector.Logs.V1;
using OpenTelemetry.Proto.Collector.Metrics.V1;
using OpenTelemetry.Proto.Collector.Trace.V1;
using OpenTelemetry.Proto.Common.V1;
using OpenTelemetry.Proto.Logs.V1;
using OpenTelemetry.Proto.Metrics.V1;
using OpenTelemetry.Proto.Resource.V1;
using OpenTelemetry.Proto.Trace.V1;
namespace Aspire.Dashboard.Otlp.Model.Serialization;
/// <summary>
/// Converts OTLP JSON types to protobuf types.
/// </summary>
internal static class OtlpJsonToProtobufConverter
{
/// <summary>
/// Converts an export trace service request from JSON to protobuf.
/// </summary>
public static ExportTraceServiceRequest ToProtobuf(OtlpExportTraceServiceRequestJson json)
{
var request = new ExportTraceServiceRequest();
if (json.ResourceSpans is not null)
{
foreach (var rs in json.ResourceSpans)
{
request.ResourceSpans.Add(ToProtobuf(rs));
}
}
return request;
}
/// <summary>
/// Converts an export logs service request from JSON to protobuf.
/// </summary>
public static ExportLogsServiceRequest ToProtobuf(OtlpExportLogsServiceRequestJson json)
{
var request = new ExportLogsServiceRequest();
if (json.ResourceLogs is not null)
{
foreach (var rl in json.ResourceLogs)
{
request.ResourceLogs.Add(ToProtobuf(rl));
}
}
return request;
}
/// <summary>
/// Converts an export metrics service request from JSON to protobuf.
/// </summary>
public static ExportMetricsServiceRequest ToProtobuf(OtlpExportMetricsServiceRequestJson json)
{
var request = new ExportMetricsServiceRequest();
if (json.ResourceMetrics is not null)
{
foreach (var rm in json.ResourceMetrics)
{
request.ResourceMetrics.Add(ToProtobuf(rm));
}
}
return request;
}
private static ResourceSpans ToProtobuf(OtlpResourceSpansJson json)
{
var resourceSpans = new ResourceSpans();
if (json.Resource is not null)
{
resourceSpans.Resource = ToProtobuf(json.Resource);
}
if (json.ScopeSpans is not null)
{
foreach (var ss in json.ScopeSpans)
{
resourceSpans.ScopeSpans.Add(ToProtobuf(ss));
}
}
if (json.SchemaUrl is not null)
{
resourceSpans.SchemaUrl = json.SchemaUrl;
}
return resourceSpans;
}
private static ScopeSpans ToProtobuf(OtlpScopeSpansJson json)
{
var scopeSpans = new ScopeSpans();
if (json.Scope is not null)
{
scopeSpans.Scope = ToProtobuf(json.Scope);
}
if (json.Spans is not null)
{
foreach (var s in json.Spans)
{
scopeSpans.Spans.Add(ToProtobuf(s));
}
}
if (json.SchemaUrl is not null)
{
scopeSpans.SchemaUrl = json.SchemaUrl;
}
return scopeSpans;
}
private static Span ToProtobuf(OtlpSpanJson json)
{
var span = new Span();
if (json.TraceId is not null)
{
span.TraceId = HexToByteString(json.TraceId);
}
if (json.SpanId is not null)
{
span.SpanId = HexToByteString(json.SpanId);
}
if (json.TraceState is not null)
{
span.TraceState = json.TraceState;
}
if (json.ParentSpanId is not null)
{
span.ParentSpanId = HexToByteString(json.ParentSpanId);
}
if (json.Flags.HasValue)
{
span.Flags = json.Flags.Value;
}
if (json.Name is not null)
{
span.Name = json.Name;
}
if (json.Kind.HasValue)
{
span.Kind = (Span.Types.SpanKind)json.Kind.Value;
}
if (json.StartTimeUnixNano.HasValue)
{
span.StartTimeUnixNano = json.StartTimeUnixNano.Value;
}
if (json.EndTimeUnixNano.HasValue)
{
span.EndTimeUnixNano = json.EndTimeUnixNano.Value;
}
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
span.Attributes.Add(ToProtobuf(attr));
}
}
span.DroppedAttributesCount = json.DroppedAttributesCount;
if (json.Events is not null)
{
foreach (var evt in json.Events)
{
span.Events.Add(ToProtobuf(evt));
}
}
span.DroppedEventsCount = json.DroppedEventsCount;
if (json.Links is not null)
{
foreach (var link in json.Links)
{
span.Links.Add(ToProtobuf(link));
}
}
span.DroppedLinksCount = json.DroppedLinksCount;
if (json.Status is not null)
{
span.Status = ToProtobuf(json.Status);
}
return span;
}
private static Span.Types.Event ToProtobuf(OtlpSpanEventJson json)
{
var evt = new Span.Types.Event();
if (json.TimeUnixNano.HasValue)
{
evt.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.Name is not null)
{
evt.Name = json.Name;
}
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
evt.Attributes.Add(ToProtobuf(attr));
}
}
evt.DroppedAttributesCount = json.DroppedAttributesCount;
return evt;
}
private static Span.Types.Link ToProtobuf(OtlpSpanLinkJson json)
{
var link = new Span.Types.Link();
if (json.TraceId is not null)
{
link.TraceId = HexToByteString(json.TraceId);
}
if (json.SpanId is not null)
{
link.SpanId = HexToByteString(json.SpanId);
}
if (json.TraceState is not null)
{
link.TraceState = json.TraceState;
}
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
link.Attributes.Add(ToProtobuf(attr));
}
}
link.DroppedAttributesCount = json.DroppedAttributesCount;
if (json.Flags.HasValue)
{
link.Flags = json.Flags.Value;
}
return link;
}
private static Status ToProtobuf(OtlpSpanStatusJson json)
{
var status = new Status();
if (json.Message is not null)
{
status.Message = json.Message;
}
if (json.Code.HasValue)
{
status.Code = (Status.Types.StatusCode)json.Code.Value;
}
return status;
}
private static ResourceLogs ToProtobuf(OtlpResourceLogsJson json)
{
var resourceLogs = new ResourceLogs();
if (json.Resource is not null)
{
resourceLogs.Resource = ToProtobuf(json.Resource);
}
if (json.ScopeLogs is not null)
{
foreach (var sl in json.ScopeLogs)
{
resourceLogs.ScopeLogs.Add(ToProtobuf(sl));
}
}
if (json.SchemaUrl is not null)
{
resourceLogs.SchemaUrl = json.SchemaUrl;
}
return resourceLogs;
}
private static ScopeLogs ToProtobuf(OtlpScopeLogsJson json)
{
var scopeLogs = new ScopeLogs();
if (json.Scope is not null)
{
scopeLogs.Scope = ToProtobuf(json.Scope);
}
if (json.LogRecords is not null)
{
foreach (var lr in json.LogRecords)
{
scopeLogs.LogRecords.Add(ToProtobuf(lr));
}
}
if (json.SchemaUrl is not null)
{
scopeLogs.SchemaUrl = json.SchemaUrl;
}
return scopeLogs;
}
private static LogRecord ToProtobuf(OtlpLogRecordJson json)
{
var logRecord = new LogRecord();
if (json.TimeUnixNano.HasValue)
{
logRecord.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.ObservedTimeUnixNano.HasValue)
{
logRecord.ObservedTimeUnixNano = json.ObservedTimeUnixNano.Value;
}
if (json.SeverityNumber.HasValue)
{
logRecord.SeverityNumber = (SeverityNumber)json.SeverityNumber.Value;
}
if (json.SeverityText is not null)
{
logRecord.SeverityText = json.SeverityText;
}
if (json.Body is not null)
{
logRecord.Body = ToProtobuf(json.Body);
}
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
logRecord.Attributes.Add(ToProtobuf(attr));
}
}
logRecord.DroppedAttributesCount = json.DroppedAttributesCount;
logRecord.Flags = json.Flags;
if (json.TraceId is not null)
{
logRecord.TraceId = HexToByteString(json.TraceId);
}
if (json.SpanId is not null)
{
logRecord.SpanId = HexToByteString(json.SpanId);
}
if (json.EventName is not null)
{
logRecord.EventName = json.EventName;
}
return logRecord;
}
private static ResourceMetrics ToProtobuf(OtlpResourceMetricsJson json)
{
var resourceMetrics = new ResourceMetrics();
if (json.Resource is not null)
{
resourceMetrics.Resource = ToProtobuf(json.Resource);
}
if (json.ScopeMetrics is not null)
{
foreach (var sm in json.ScopeMetrics)
{
resourceMetrics.ScopeMetrics.Add(ToProtobuf(sm));
}
}
if (json.SchemaUrl is not null)
{
resourceMetrics.SchemaUrl = json.SchemaUrl;
}
return resourceMetrics;
}
private static ScopeMetrics ToProtobuf(OtlpScopeMetricsJson json)
{
var scopeMetrics = new ScopeMetrics();
if (json.Scope is not null)
{
scopeMetrics.Scope = ToProtobuf(json.Scope);
}
if (json.Metrics is not null)
{
foreach (var m in json.Metrics)
{
scopeMetrics.Metrics.Add(ToProtobuf(m));
}
}
if (json.SchemaUrl is not null)
{
scopeMetrics.SchemaUrl = json.SchemaUrl;
}
return scopeMetrics;
}
private static Metric ToProtobuf(OtlpMetricJson json)
{
var metric = new Metric();
if (json.Name is not null)
{
metric.Name = json.Name;
}
if (json.Description is not null)
{
metric.Description = json.Description;
}
if (json.Unit is not null)
{
metric.Unit = json.Unit;
}
if (json.Gauge is not null)
{
metric.Gauge = ToProtobuf(json.Gauge);
}
else if (json.Sum is not null)
{
metric.Sum = ToProtobuf(json.Sum);
}
else if (json.Histogram is not null)
{
metric.Histogram = ToProtobuf(json.Histogram);
}
else if (json.ExponentialHistogram is not null)
{
metric.ExponentialHistogram = ToProtobuf(json.ExponentialHistogram);
}
else if (json.Summary is not null)
{
metric.Summary = ToProtobuf(json.Summary);
}
if (json.Metadata is not null)
{
foreach (var kv in json.Metadata)
{
metric.Metadata.Add(ToProtobuf(kv));
}
}
return metric;
}
private static Gauge ToProtobuf(OtlpGaugeJson json)
{
var gauge = new Gauge();
if (json.DataPoints is not null)
{
foreach (var dp in json.DataPoints)
{
gauge.DataPoints.Add(ToProtobuf(dp));
}
}
return gauge;
}
private static Sum ToProtobuf(OtlpSumJson json)
{
var sum = new Sum();
if (json.DataPoints is not null)
{
foreach (var dp in json.DataPoints)
{
sum.DataPoints.Add(ToProtobuf(dp));
}
}
if (json.AggregationTemporality.HasValue)
{
sum.AggregationTemporality = (AggregationTemporality)json.AggregationTemporality.Value;
}
sum.IsMonotonic = json.IsMonotonic;
return sum;
}
private static Histogram ToProtobuf(OtlpHistogramJson json)
{
var histogram = new Histogram();
if (json.DataPoints is not null)
{
foreach (var dp in json.DataPoints)
{
histogram.DataPoints.Add(ToProtobuf(dp));
}
}
if (json.AggregationTemporality.HasValue)
{
histogram.AggregationTemporality = (AggregationTemporality)json.AggregationTemporality.Value;
}
return histogram;
}
private static ExponentialHistogram ToProtobuf(OtlpExponentialHistogramJson json)
{
var histogram = new ExponentialHistogram();
if (json.DataPoints is not null)
{
foreach (var dp in json.DataPoints)
{
histogram.DataPoints.Add(ToProtobuf(dp));
}
}
if (json.AggregationTemporality.HasValue)
{
histogram.AggregationTemporality = (AggregationTemporality)json.AggregationTemporality.Value;
}
return histogram;
}
private static Summary ToProtobuf(OtlpSummaryJson json)
{
var summary = new Summary();
if (json.DataPoints is not null)
{
foreach (var dp in json.DataPoints)
{
summary.DataPoints.Add(ToProtobuf(dp));
}
}
return summary;
}
private static NumberDataPoint ToProtobuf(OtlpNumberDataPointJson json)
{
var dataPoint = new NumberDataPoint();
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
dataPoint.Attributes.Add(ToProtobuf(attr));
}
}
if (json.StartTimeUnixNano.HasValue)
{
dataPoint.StartTimeUnixNano = json.StartTimeUnixNano.Value;
}
if (json.TimeUnixNano.HasValue)
{
dataPoint.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.AsDouble.HasValue)
{
dataPoint.AsDouble = json.AsDouble.Value;
}
else if (json.AsInt.HasValue)
{
dataPoint.AsInt = json.AsInt.Value;
}
if (json.Exemplars is not null)
{
foreach (var ex in json.Exemplars)
{
dataPoint.Exemplars.Add(ToProtobuf(ex));
}
}
dataPoint.Flags = json.Flags;
return dataPoint;
}
private static HistogramDataPoint ToProtobuf(OtlpHistogramDataPointJson json)
{
var dataPoint = new HistogramDataPoint();
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
dataPoint.Attributes.Add(ToProtobuf(attr));
}
}
if (json.StartTimeUnixNano.HasValue)
{
dataPoint.StartTimeUnixNano = json.StartTimeUnixNano.Value;
}
if (json.TimeUnixNano.HasValue)
{
dataPoint.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.Count.HasValue)
{
dataPoint.Count = json.Count.Value;
}
if (json.Sum.HasValue)
{
dataPoint.Sum = json.Sum.Value;
}
if (json.BucketCounts is not null)
{
foreach (var bc in json.BucketCounts)
{
dataPoint.BucketCounts.Add(ulong.Parse(bc, System.Globalization.CultureInfo.InvariantCulture));
}
}
if (json.ExplicitBounds is not null)
{
foreach (var eb in json.ExplicitBounds)
{
dataPoint.ExplicitBounds.Add(eb);
}
}
if (json.Exemplars is not null)
{
foreach (var ex in json.Exemplars)
{
dataPoint.Exemplars.Add(ToProtobuf(ex));
}
}
dataPoint.Flags = json.Flags;
if (json.Min.HasValue)
{
dataPoint.Min = json.Min.Value;
}
if (json.Max.HasValue)
{
dataPoint.Max = json.Max.Value;
}
return dataPoint;
}
private static ExponentialHistogramDataPoint ToProtobuf(OtlpExponentialHistogramDataPointJson json)
{
var dataPoint = new ExponentialHistogramDataPoint();
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
dataPoint.Attributes.Add(ToProtobuf(attr));
}
}
if (json.StartTimeUnixNano.HasValue)
{
dataPoint.StartTimeUnixNano = json.StartTimeUnixNano.Value;
}
if (json.TimeUnixNano.HasValue)
{
dataPoint.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.Count.HasValue)
{
dataPoint.Count = json.Count.Value;
}
if (json.Sum.HasValue)
{
dataPoint.Sum = json.Sum.Value;
}
dataPoint.Scale = json.Scale;
if (json.ZeroCount.HasValue)
{
dataPoint.ZeroCount = json.ZeroCount.Value;
}
if (json.Positive is not null)
{
dataPoint.Positive = ToProtobuf(json.Positive);
}
if (json.Negative is not null)
{
dataPoint.Negative = ToProtobuf(json.Negative);
}
dataPoint.Flags = json.Flags;
if (json.Exemplars is not null)
{
foreach (var ex in json.Exemplars)
{
dataPoint.Exemplars.Add(ToProtobuf(ex));
}
}
if (json.Min.HasValue)
{
dataPoint.Min = json.Min.Value;
}
if (json.Max.HasValue)
{
dataPoint.Max = json.Max.Value;
}
dataPoint.ZeroThreshold = json.ZeroThreshold;
return dataPoint;
}
private static ExponentialHistogramDataPoint.Types.Buckets ToProtobuf(OtlpExponentialHistogramBucketsJson json)
{
var buckets = new ExponentialHistogramDataPoint.Types.Buckets();
buckets.Offset = json.Offset;
if (json.BucketCounts is not null)
{
foreach (var bc in json.BucketCounts)
{
buckets.BucketCounts.Add(ulong.Parse(bc, System.Globalization.CultureInfo.InvariantCulture));
}
}
return buckets;
}
private static SummaryDataPoint ToProtobuf(OtlpSummaryDataPointJson json)
{
var dataPoint = new SummaryDataPoint();
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
dataPoint.Attributes.Add(ToProtobuf(attr));
}
}
if (json.StartTimeUnixNano.HasValue)
{
dataPoint.StartTimeUnixNano = json.StartTimeUnixNano.Value;
}
if (json.TimeUnixNano.HasValue)
{
dataPoint.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.Count.HasValue)
{
dataPoint.Count = json.Count.Value;
}
dataPoint.Sum = json.Sum;
if (json.QuantileValues is not null)
{
foreach (var qv in json.QuantileValues)
{
dataPoint.QuantileValues.Add(ToProtobuf(qv));
}
}
dataPoint.Flags = json.Flags;
return dataPoint;
}
private static SummaryDataPoint.Types.ValueAtQuantile ToProtobuf(OtlpValueAtQuantileJson json)
{
var valueAtQuantile = new SummaryDataPoint.Types.ValueAtQuantile();
valueAtQuantile.Quantile = json.Quantile;
valueAtQuantile.Value = json.Value;
return valueAtQuantile;
}
private static Exemplar ToProtobuf(OtlpExemplarJson json)
{
var exemplar = new Exemplar();
if (json.FilteredAttributes is not null)
{
foreach (var attr in json.FilteredAttributes)
{
exemplar.FilteredAttributes.Add(ToProtobuf(attr));
}
}
if (json.TimeUnixNano.HasValue)
{
exemplar.TimeUnixNano = json.TimeUnixNano.Value;
}
if (json.AsDouble.HasValue)
{
exemplar.AsDouble = json.AsDouble.Value;
}
else if (json.AsInt.HasValue)
{
exemplar.AsInt = json.AsInt.Value;
}
if (json.SpanId is not null)
{
exemplar.SpanId = HexToByteString(json.SpanId);
}
if (json.TraceId is not null)
{
exemplar.TraceId = HexToByteString(json.TraceId);
}
return exemplar;
}
private static Resource ToProtobuf(OtlpResourceJson json)
{
var resource = new Resource();
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
resource.Attributes.Add(ToProtobuf(attr));
}
}
resource.DroppedAttributesCount = json.DroppedAttributesCount;
return resource;
}
private static InstrumentationScope ToProtobuf(OtlpInstrumentationScopeJson json)
{
var scope = new InstrumentationScope();
if (json.Name is not null)
{
scope.Name = json.Name;
}
if (json.Version is not null)
{
scope.Version = json.Version;
}
if (json.Attributes is not null)
{
foreach (var attr in json.Attributes)
{
scope.Attributes.Add(ToProtobuf(attr));
}
}
scope.DroppedAttributesCount = json.DroppedAttributesCount;
return scope;
}
private static KeyValue ToProtobuf(OtlpKeyValueJson json)
{
var kv = new KeyValue();
if (json.Key is not null)
{
kv.Key = json.Key;
}
if (json.Value is not null)
{
kv.Value = ToProtobuf(json.Value);
}
return kv;
}
private static AnyValue ToProtobuf(OtlpAnyValueJson json)
{
var anyValue = new AnyValue();
if (json.StringValue is not null)
{
anyValue.StringValue = json.StringValue;
}
else if (json.BoolValue.HasValue)
{
anyValue.BoolValue = json.BoolValue.Value;
}
else if (json.IntValue.HasValue)
{
anyValue.IntValue = json.IntValue.Value;
}
else if (json.DoubleValue.HasValue)
{
anyValue.DoubleValue = json.DoubleValue.Value;
}
else if (json.ArrayValue is not null)
{
anyValue.ArrayValue = ToProtobuf(json.ArrayValue);
}
else if (json.KvlistValue is not null)
{
anyValue.KvlistValue = ToProtobuf(json.KvlistValue);
}
else if (json.BytesValue is not null)
{
anyValue.BytesValue = ByteString.FromBase64(json.BytesValue);
}
return anyValue;
}
private static ArrayValue ToProtobuf(OtlpArrayValueJson json)
{
var arrayValue = new ArrayValue();
if (json.Values is not null)
{
foreach (var v in json.Values)
{
arrayValue.Values.Add(ToProtobuf(v));
}
}
return arrayValue;
}
private static KeyValueList ToProtobuf(OtlpKeyValueListJson json)
{
var kvList = new KeyValueList();
if (json.Values is not null)
{
foreach (var kv in json.Values)
{
kvList.Values.Add(ToProtobuf(kv));
}
}
return kvList;
}
/// <summary>
/// Converts a hexadecimal string to a ByteString.
/// </summary>
/// <param name="hex">The hexadecimal string to convert.</param>
/// <returns>A ByteString containing the decoded bytes.</returns>
/// <exception cref="ArgumentException">Thrown when the hex string has an odd length.</exception>
internal static ByteString HexToByteString(string hex)
{
if (string.IsNullOrEmpty(hex))
{
return ByteString.Empty;
}
if (hex.Length % 2 != 0)
{
throw new ArgumentException("Hex string must have an even length.", nameof(hex));
}
var hexSpan = hex.AsSpan();
var bytes = new byte[hex.Length / 2];
for (var i = 0; i < bytes.Length; i++)
{
bytes[i] = byte.Parse(hexSpan.Slice(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
}
return ByteString.CopyFrom(bytes);
}
}
/// <summary>
/// Converts protobuf types to OTLP JSON types.
/// </summary>
internal static class OtlpProtobufToJsonConverter
{
/// <summary>
/// Converts an export trace service response from protobuf to JSON.
/// </summary>
public static OtlpExportTraceServiceResponseJson ToJson(ExportTraceServiceResponse response)
{
var json = new OtlpExportTraceServiceResponseJson();
if (response.PartialSuccess is not null && (response.PartialSuccess.RejectedSpans > 0 || !string.IsNullOrEmpty(response.PartialSuccess.ErrorMessage)))
{
json.PartialSuccess = new OtlpExportTracePartialSuccessJson
{
RejectedSpans = response.PartialSuccess.RejectedSpans,
ErrorMessage = response.PartialSuccess.ErrorMessage
};
}
return json;
}
/// <summary>
/// Converts an export logs service response from protobuf to JSON.
/// </summary>
public static OtlpExportLogsServiceResponseJson ToJson(ExportLogsServiceResponse response)
{
var json = new OtlpExportLogsServiceResponseJson();
if (response.PartialSuccess is not null && (response.PartialSuccess.RejectedLogRecords > 0 || !string.IsNullOrEmpty(response.PartialSuccess.ErrorMessage)))
{
json.PartialSuccess = new OtlpExportLogsPartialSuccessJson
{
RejectedLogRecords = response.PartialSuccess.RejectedLogRecords,
ErrorMessage = response.PartialSuccess.ErrorMessage
};
}
return json;
}
/// <summary>
/// Converts an export metrics service response from protobuf to JSON.
/// </summary>
public static OtlpExportMetricsServiceResponseJson ToJson(ExportMetricsServiceResponse response)
{
var json = new OtlpExportMetricsServiceResponseJson();
if (response.PartialSuccess is not null && (response.PartialSuccess.RejectedDataPoints > 0 || !string.IsNullOrEmpty(response.PartialSuccess.ErrorMessage)))
{
json.PartialSuccess = new OtlpExportMetricsPartialSuccessJson
{
RejectedDataPoints = response.PartialSuccess.RejectedDataPoints,
ErrorMessage = response.PartialSuccess.ErrorMessage
};
}
return json;
}
}
|