-1

I have this iOS project that embed an unity scene as a view. This problem occur on a device that set a localize that number has comma , as decimal seperator, rather than a point .

I sent a parameter string from native code (swift) to unity. Unity then parse the string from iOS, make a new parameters to embed in another http request.

this is the log in xcode:

param from iOS: 615d48538edef900126a784e***616957b6f59835001137ae7b***98.42519685039369***100.06561679790026
Request Object:
- distanceFromCeiling: 9,84251968503937E+15
- distanceFromSideWall: 1,000656167979E+16
  - Obj: : UpdateRequest
  - Obj.distanceFromCeiling: : 9,84251968503937E+15
  - Obj.distanceFromSideWall: : 1,000656167979E+16
DATA request {"distanceFromCeiling":9842519685039368.0,"distanceFromSideWall":10006561679790026.0}

I expected that I send 98.42519685039369, 100.06561679790026, but for some reason, it becomes 9842519685039368.0, 10006561679790026.0 in the data request json string.

How to fix this? I have another log from a different device, with localize en-US and it seems the problem doesn't occur here.

The code in Unity (search for is_the_problem_here to jump right to the quetionable line:

using System;
using System.Collections;
using System.Collections.Generic;
using Lean.Touch;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.UI;
using BestHTTP;
using System.Text;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using UnityEngine.UIElements;

public class GameManager : MonoBehaviour
{

// Other functions
// ====================================

void callApiFromIOS(string param)
{
    Debug.Log("param from iOS: " + param);

    string[] str = param.Split(new string[] { "***" }, StringSplitOptions.None);

    // is_the_problem_here
    System.Nullable <double> distanceFromCeiling = null;
    System.Nullable<double> distanceFromSideWall = null;

    if (str != null && str.Length >= 4)
    {
        string ceiling = str[2];
        if (!ceiling.Equals("null"))
        {
            // is_the_problem_here
            distanceFromCeiling = Convert.ToDouble(ceiling);
        }
       
        string sideWall = str[3];
        if (!sideWall.Equals("null"))
        {
            // is_the_problem_here
            distanceFromSideWall = Convert.ToDouble(sideWall);
        }
    }

    Debug.Log("Request Object: ");
    Debug.Log("- distanceFromCeiling: " + distanceFromCeiling);
    Debug.Log("- distanceFromSideWall: " + distanceFromSideWall);

    UpdateRequest updateRequest = new UpdateRequest(distanceFromCeiling, distanceFromSideWall);
    Debug.Log("  - Obj: : " + updateRequest);
    Debug.Log("  - Obj.distanceFromCeiling: : " + updateRequest.distanceFromCeiling);
    Debug.Log("  - Obj.distanceFromSideWall: : " + updateRequest.distanceFromSideWall);

    // is_the_problem_here
    string data = JsonConvert.SerializeObject(updateRequest);

    Debug.Log("DATA request " + data);

     // the rest of the code is set the json data as request body and send http.
}

The other log:

param from iOS: 615d48538edef900126a784e***616957b6f59835001137ae7b***98.42519685039369***100.06561679790026

Request Object: 
- distanceFromCeiling: 98.4251968503937
- distanceFromSideWall: 100.0656167979
  - Obj: : UpdateRequest
  - Obj.distanceFromCeiling: : 98.4251968503937
  - Obj.distanceFromSideWall: : 100.0656167979
DATA request {"distanceFromCeiling":98.425196850393689,"distanceFromSideWall":100.06561679790026}

** UPDATE **

I've tried following this link but it's still not working as expect (same output as the first)

var converter = new FormattedDecimalConverter(CultureInfo.GetCultureInfo("en-US"));
string data = JsonConvert.SerializeObject(updateRequest, converter);

The converter class:

using System;
using System.Globalization;
using Newtonsoft.Json;

internal class FormattedDecimalConverter : JsonConverter
{
    private CultureInfo culture;

    public FormattedDecimalConverter(CultureInfo culture)
    {
        this.culture = culture;
    }

    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(Convert.ToString(value, culture));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Sorry for being clueless. What am I doing wrong here?

** UPDATE **

so changing Convert.toDouble() to double.Parse(ceiling, CultureInfo.InvariantCulture) fix the problem. What weird issue.

Eddie
  • 1,903
  • 2
  • 21
  • 46
  • See the duplicate link ... you most probably want to use the `CultureInfo.InvariantCulture` – derHugo Nov 03 '21 at 05:53
  • @derHugo sorry for being new in this, but where is the problem? as you can see, even the log is not correct (98.4251968503937 => 9,84251968503937E+15) and then output incorrect json value (9842519685039368.0) – Eddie Nov 03 '21 at 07:23
  • 1
    `9,84251968503937E+15` is just another way of printing out `9842519685039368.0`. In some cultures (e.g. french, German, etc) the `.` character is not considered the decimal splitter but rather used for decimal groups (e.g. 1.000 is not one but one thousand). Instead of `Convert` you rather want to use e.g. `double.Parse(ceiling, CultureInfo.InvariantCulture)` ... otherwise they will use the language settings of according device the code is executed on – derHugo Nov 03 '21 at 07:31
  • 2
    It might have been in both. But actually I just read up and `CultureInfo.InvariantCulture` is what `JsonConvert` uses by default anyway .. so it is in `Convert.ToDouble` ;) – derHugo Nov 03 '21 at 08:02

1 Answers1

2

Per default Convert.ToDouble(string) uses the language/region settings of the device the code is running on.

In some cultures the . character is not considered as the decimal splitter but rather the decimal group character.

=> e.g. 1.000 is not one but rather one thousand!


If dealing with strings across multiple devices you should always make sure to use the cultured overload Convert.ToDouble(string, IFormatProvider) and pass in the CultureInfo.InvariantCulture which is based on en-us.

string ceiling = str[2];
if (!ceiling.Equals("null"))
{
    // is_the_problem_here
    distanceFromCeiling = Convert.ToDouble(ceiling, CultureInfo.InvariantCulture);
}
   
string sideWall = str[3];
if (!sideWall.Equals("null"))
{
    // is_the_problem_here
    distanceFromSideWall = Convert.ToDouble(sideWall, CultureInfo.InvariantCulture);
}
derHugo
  • 83,094
  • 9
  • 75
  • 115