Чтение номера Json в виде строки

Что мне нужно:

  • Десериализовать строку Json в объект
  • Сделайте что-нибудь
  • верните его как строку в конце

В чем проблема? Когда Json имеет свойство типа {"foobar" : 0.0000000000000000001}, оно преобразуется в число, но затем в виде строки оно представляется в научной записи как 1E-19.

Есть ли способ прочитать число из json в виде строки или позже преобразовать такое число в ту же самую строку без потери точности? Точность не фиксирована, и число в json может встречаться в любом свойстве. Использование Newtonsoft предпочтительнее (если это возможно) вместо System.Text.Json.

Я пытался

  1. создать CustomJsonConverter от Newtonsoft
  2. используйте System.Text.Json.JsonNumberHandling.WriteAsString
  3. преобразование числа с плавающей запятой в строку но ни одно из этих решений не сработало
  4. Я не смог найти способ обрабатывать любое числовое значение как строку, не знаю, возможно ли это вообще в таком конвертере.
  5. Я думаю, что этот работает только в одном направлении, от объекта к строке, и это не то, что я ищу.
  6. такое преобразование не сработало, потому что оно потеряло точность или сделало «чрезмерную точность» (0,00...01 было записано как 0,00...0989919999 или что-то в этом роде), или добавили дополнительные нули в конце - зависит от форматирования я использовал

В идеале я хотел бы преобразовать строку типа {"foobar": 21.37} в объект со свойством foobar = "21.37", чтобы быть уверенным, что любая точность не будет потеряна после покрытия между числом/строкой.

Минимальный воспроизводимый пример

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

var json = """
    [{
        "foobar1": 21.37000,
        "foobar2": 0.0000000000000000001000,
        "foobar3": 1.1000E-19,
        "aReallyBigInteger" : 1111199999999999999999999999999999999911111
    }]
    """;
var input = JsonConvert.DeserializeObject<JToken>(json);

Console.WriteLine(input);
/*
Expected result:
[
  {
    "foobar1": "21.37000",
    "foobar2": "0.0000000000000000001000",
    "foobar3": "1.1000E-19",
    "aReallyBigInteger": "1111199999999999999999999999999999999911111"
  }
]
Current Behavior:
[
  {
    "foobar1": 21.37,
    "foobar2": 1E-19,
    "foobar3": 1.1E-19,
    "aReallyBigInteger": 1111199999999999999999999999999999999911111
  }
]
*/

не очень понятно. Ограничение точности связано с внутренним представлением числа (т. е. двойным). Строка from/to не ограничивает точность, если не используется какая-либо схема форматирования. Однако я не думаю, что кто-то из них используется.

Mario Vernari 06.05.2024 08:00

Проведите тест здесь: binaryconvert.com/…

Mario Vernari 06.05.2024 08:05

Почему вас волнует, как число с плавающей запятой представлено в json? Есть ли у вас основания полагать, что он теряет точность во время цикла сериализации/десериализации?

Fildor 06.05.2024 08:18

С Json.NET это невозможно сделать. JsonTextReader преобразует числа JSON в double, decimal, int или какой-либо другой числовой тип, а затем отбрасывает исходное текстовое представление, которое никогда не становится общедоступным для вызывающих абонентов. Вы могли бы попробовать разветвить Json.NET и изменить JsonTextWriter, чтобы сохранить символьное представление чисел, но я подозреваю, что это будет нетривиально.

dbc 06.05.2024 08:22

Требования клиентов - в конце мне нужно показать ту же строку, что и в начале, поэтому, когда клиенты ставят 0,0001, он хочет видеть позже 0,0001, а не 1E-5 (я возвращаю строку в конце)

MatteoDelOmbra 06.05.2024 08:23

Однако это можно сделать с помощью System.Text.Json. JsonElement и JsonNode сохраняют базовые представления символов токенов JSON, поэтому при их обратной записи они останутся неизменными. Возможно, это удовлетворит ваши потребности?

dbc 06.05.2024 08:23

@MarioVernari это ограничивает - простой тест double x = 0.000000000000000000000000000000000000000000000000000000001; строка xStr = x.ToString(); Console.WriteLine(xStr); //он вернет 1E-57 //он не вернет 0.000000000000000000000000000000000000000000000000000000001

MatteoDelOmbra 06.05.2024 08:25

@MatteoDelOmbra Это вообще превышает точность double?

Fildor 06.05.2024 08:26

@dbc Я пробовал это, но в конце вернулось число, а не строка. можешь подробнее, как это сделать, может я что-то не так сделал

MatteoDelOmbra 06.05.2024 08:27

а) это число не может быть представлено в виде double (оно поддерживает только 17 значащих цифр) б) 1E-57 и 0,0000000000000000000000000000000000000000000000000000001 - это буквально одно и то же числовое значение (просто разные представления, так же, как 0x10, 0o20 и 16 — это такое же количество)

knittl 06.05.2024 08:27

«в конце мне нужно показать ту же строку, что и была» - затем передать как строку ??

Fildor 06.05.2024 08:28

@MatteoDelOmbra - можешь ли ты тогда поделиться минимально воспроизводимым примером? Конечно, типы double и decimal не могут сохранять числовое форматирование, но если вы прочитаете JsonNode, я думаю, оно запомнится.

dbc 06.05.2024 08:28

^^ И даже если это не так: вы можете ввести тип, у которого есть свойство для числового значения и отдельное свойство для (принудительного) строкового представления. Или значение и формат?

Fildor 06.05.2024 08:32

Вот демонстрация JsonNode.Parse() сохранения числового форматирования: dotnetfiddle.net/JHO53j.

dbc 06.05.2024 08:33

Хм, ответ Шинго был правильным: Json.NET сохраняет числовое форматирование для свойств, объявленных как строки, см. dotnetfiddle.net/Lwrvdv. Конечно, они записываются обратно в виде строк с кавычками. Я почти уверен, что это невозможно сделать с помощью конвертера, сериализатор использует для этого частный API.

dbc 06.05.2024 08:41

Пожалуйста, уточните вашу конкретную проблему или предоставьте дополнительную информацию, чтобы выделить именно то, что вам нужно. Поскольку сейчас написано, трудно точно сказать, о чем вы спрашиваете.

Community 06.05.2024 09:05

«в конце мне нужно показать ту же строку, что и была», затем отформатируйте ее таким образом. Существует разница между «числом» и его представлением. 0,00001 и 1E-5 — одно и то же число, но в разных представлениях. Если вам (или вашему клиенту) нравится представление X, вы сами должны позаботиться о том, чтобы ценность была представлена ​​именно таким образом. Система имеет определенное поведение по умолчанию. Если вам это не нравится, вам придется это переопределить...

derpirscher 06.05.2024 11:06

@derpirscher да, форматирование, это моя проблема - я указал это, и чтение числа в виде строки было лишь одной из идей, как, я думаю, это можно решить - если есть способ отформатировать конечную строку, чтобы она всегда выглядела так же, как входная строка, тогда я' я ок с этим

MatteoDelOmbra 06.05.2024 11:28

@dbc Я добавил воспроизводимый пример минимума. Об ответе Шинго — это своего рода результат, который я ищу, но я не знаю, какой формат данных будет предоставлен — такое число может встречаться в любом свойстве, которое мне просто нужно обработать его и вернуть такой номер без изменений

MatteoDelOmbra 06.05.2024 11:31

@dbc о предоставленном вами dotnetfiddle. В окончательном варианте данные json по-прежнему представляют собой числа (без кавычек). Я использую другие функции newtonsoft, которые я не смог найти в System.Text.Json (поскольку они перебирают всех потомков), поэтому использование System. Json для меня приемлем, только если я могу изменить эти числа на строки, создать из них строку Json, а затем десериализовать новую строку JSON с помощью Newtonsoft.

MatteoDelOmbra 06.05.2024 11:34

@dbc ох, и что касается тебя, снова поиграй на скрипке. Теоретически, я думаю, это должно сработать, если я добавлю туда NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.WriteAsStr‌ в параметры сериализатора, но он по-прежнему выдает числа (а не строку в кавычках) на выходе, поэтому я думаю, что этот параметр не подходит вообще работаю

MatteoDelOmbra 06.05.2024 11:49

Вам нужно десериализовать в JToken или вы можете десериализовать в фиксированную модель данных?

dbc 06.05.2024 17:16

@dbc Нет, я не знаю, какой будет структура данных, поэтому я не могу использовать фиксированную модель данных

MatteoDelOmbra 07.05.2024 11:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
23
172
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я попытался создать собственный класс Converter, подобный показанному ниже, и использовал его для десериализации строки JSON, содержащей числа, и, похоже, он возвращает числа в виде строки без потери точности. Пожалуйста, попробуйте и дайте мне знать, если это сработает.

public class NumberToStringConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(decimal) || objectType == typeof(double) || objectType == typeof(float);
    }
    
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Float || token.Type == JTokenType.Integer)
        {
            return token.ToString();
        }
        throw new JsonSerializationException($"Unexpected token type: {token.Type}");
    }
    
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(value.ToString());
    }
}

Я использовал его для такого примера var json = @"[{ ""foo"" : 0.1, ""bar"" : 1.00, ""baz"" : 0.0000000000000000000000000000000000000000000000000000001 }]"; Но в CanConvert() он вернул false, поскольку обнаруженный тип был JToken.

MatteoDelOmbra 06.05.2024 11:11

@MatteoDelOmbra, вы не сможете этого добиться, если не напишете свой собственный анализатор JSON и не будете использовать библиотеку «бесконечной точности» для обработки чисел. На мой взгляд, это ограничение ваших клиентов, поскольку научная запись в JSON полностью легальна: json.org/json-en.html

Mario Vernari 06.05.2024 11:27

@MatteoDelOmbra - JSON, который вы предоставили здесь в своем комментарии, представляет собой массив объектов, а тот, который вы упомянули в своем вопросе, представляет собой один объект, поэтому вам нужно будет соответствующим образом настроить структуру вашего класса в соответствии с требованиями вашего клиента для его анализа. . Внесите соответствующие изменения, и этот метод отлично подойдет вам.

Yash Gupta 06.05.2024 11:55

@YashGupta Я также запускаю его с помощью var json = @"{ ""foo"" : 0.1, ""bar"" : 1.00, ""baz"" : 0.0000000000000000000000000000000000000000000000000000001 }"; но поведение такое же

MatteoDelOmbra 06.05.2024 12:08

это может вам помочь stackoverflow.com/questions/78339575/…

Power Mouse 06.05.2024 21:01
Ответ принят как подходящий

МОЕ РЕШЕНИЕ

Вонючий код, но тот, который сработал для меня.

  1. десериализовать json с помощью System.Text.Json
  2. сериализуйте его с помощью специального конвертера, написанного Groxan здесь обратно в json
  3. Новый json можно десериализовать с помощью стандартного Newtonsoft, а наши числа уже являются строками и обрабатываются как строки.

Вероятно, ужасная эффективность, но только одна, которая соответствует моим требованиям.

Другие вопросы по теме

Похожие вопросы

Почему при передаче первого строкового элемента в массив bool длина этого элемента всегда равна длине массива?
Как сохранить размер окна просмотра?
Отложенная запись WriteableBitmap в фоновом потоке вызывает мерцание
Как пометить параметр атрибутом в динамически создаваемом лямбда-выражении?
Когда я пытаюсь построить Docker, я получаю, что каждый из моих проектов csharp не может вычислить кеш (то есть не может быть найден)
Эквивалент ConfigurationApiBehaviorOptions() для минимальных API
Уменьшите количество условных операторов (4), используемых в выражении (максимум 3) в предикате Expression<Func<T,bool>
Microsoft.Office.Interop.Excel не работает после перехода на PackageReference
Регулярное выражение не разбивает строку, разделенную точкой с запятой, в C#, если первое совпадение пусто
Как изменить громкость преобразования текста в речь для объектов SpeechSynthesizer в Azure CognitiveServices SDK без изменения громкости системы в С#