Сохраняйте порядок ключей JSON во время преобразования JSON в CSV - PullRequest
75 голосов
/ 23 декабря 2010

Я использую библиотеку JSON, предоставленную здесь http://www.json.org/java/index.html, чтобы преобразовать строку json, которую я должен, в CSV.Но у меня проблема в том, что порядок ключей теряется после преобразования.

Это код преобразования:

    JSONObject jo = new JSONObject(someString);
    JSONArray ja = jo.getJSONArray("items");
    String s = CDL.toString(ja);
    System.out.println(s);

Это содержимое "someString":

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ]
}

Это результат:

WO,QU,WR,QA,NO
hasd,asd,qwe,end,qwer

В то время как я ожидаю, чтобы сохранить порядок ключей:

WR,QU,QA,WO,NO
qwe,asd,end,hasd,qwer

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

Ответы [ 16 ]

91 голосов
/ 23 декабря 2010

Есть (хакерские) способы сделать это ... но вы не должны.

В JSON объект определяется следующим образом:

Объект - это неупорядоченный набор пар имя / значение.

См. http://json.org.

Большинство реализаций JSON не прилагают усилий для сохранения порядка пар имя / значение объекта, поскольку это (по определению) не имеет значения.

Если вы хотите сохранить порядок, вам необходимо переопределить структуру данных; например,

{
    "items":
    [
        [
            {"WR":"qwe"},
            {"QU":"asd"},
            {"QA":"end"},
            {"WO":"hasd"},
            {"NO":"qwer"}
        ],
    ]
}

или более просто:

{
    "items":
    [
        {"WR":"qwe"},
        {"QU":"asd"},
        {"QA":"end"},
        {"WO":"hasd"},
        {"NO":"qwer"}
    ]
}

Followup

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

Вам нужно серьезно поговорить с тем, кто разработал эту файловую структуру, и не позволит вам ее изменить. Это / они совершенно не правы. Вам нужно , чтобы убедить их.

Если они действительно не позволят вам изменить его:

  • Вы должны настаивать на , а не , называя это JSON ... потому что это не так.
  • Вы должны указать, что вам придется специально писать / изменять код для обработки этого формата "не JSON" ... если только вы не найдете какую-либо реализацию JSON, которая сохраняет порядок. Если они платный клиент, убедитесь, что они оплачивают эту дополнительную работу, которую вы должны сделать.
  • Вы должны указать, что если "не JSON" нужно использовать каким-либо другим инструментом, это будет проблематично. Действительно, эта проблема будет возникать снова и снова ...

Такие вещи очень плохие. С одной стороны, ваше программное обеспечение будет нарушать устоявшуюся / давнюю спецификацию, разработанную для обеспечения совместимости. С другой стороны, придурки, которые разработали этот хромой (не JSON!) Формат файла, вероятно, отвлекаются от систем других людей и т. Д., Поскольку системы не могут справиться с их бессмыслицей.

UPDATE

Также стоит прочитать, что JSON RFC (RFC 7159) говорит по этому вопросу. Вот некоторые выдержки:

За годы, прошедшие после публикации RFC 4627, JSON нашел очень широкое распространение. Этот опыт выявил определенные закономерности, которые, в то время как позволено его спецификациями, вызвало совместимость проблемы.

JavaScript Object Notation (JSON) - это текстовый формат для сериализация структурированных данных. ...

JSON может представлять четыре примитивных типа (строки, числа, логические значения, и ноль) и два структурированных типа (объекты и массивы).

Объект является неупорядоченным набором из нуля или более имени / значения пары, где имя - это строка, а значение - строка, число, логическое значение, ноль, объект или массив.

Было обнаружено, что библиотеки синтаксического анализа JSON различаются по не они делают порядок членов объекта видимым для вызова программного обеспечения. Реализации, поведение которых не зависит от члена порядок будет совместим в том смысле, что они не будут затронуты этими различиями.

46 голосов
/ 02 ноября 2011

Поддерживать порядок довольно просто. У меня была такая же проблема с поддержанием порядка от уровня БД до уровня пользовательского интерфейса.

Открыть файл JSONObject.java. Он внутренне использует HashMap, который не поддерживает порядок.

Измените его на LinkedHashMap:

    //this.map = new HashMap();
    this.map = new LinkedHashMap();

Это сработало для меня. Дай мне знать в комментариях. Я предлагаю, чтобы сама библиотека JSON имела другой класс JSONObject, который поддерживает порядок, например JSONOrderdObject.java. Я очень плох в выборе имен.

21 голосов
/ 24 января 2013

JSONObject.java берет любую карту, которую вы проходите. Это может быть LinkedHashMap или TreeMap, и это займет hashmap только тогда, когда карта пуста.

Вот конструктор класса JSONObject.java, который будет проверять карту.

 public JSONObject(Map paramMap)
  {
    this.map = (paramMap == null ? new HashMap() : paramMap);
  }

Таким образом, перед построением объекта json создайте LinkedHashMap и затем передайте его конструктору следующим образом:

LinkedHashMap<String, String> jsonOrderedMap = new LinkedHashMap<String, String>();

jsonOrderedMap.put("1","red");
jsonOrderedMap.put("2","blue");
jsonOrderedMap.put("3","green");

JSONObject orderedJson = new JSONObject(jsonOrderedMap);

JSONArray jsonArray = new JSONArray(Arrays.asList(orderedJson));

System.out.println("Ordered JSON Fianl CSV :: "+CDL.toString(jsonArray));

Так что нет необходимости менять класс JSONObject.java. Надеюсь, это кому-нибудь поможет.

13 голосов
/ 11 июня 2014

Более подробное, но широко применимое решение этой проблемы - использовать пару структур данных: список, содержащий порядок, и карту, содержащую отношения.

Например:

{
    "items":
    [
        {
            "WR":"qwe",
            "QU":"asd",
            "QA":"end",
            "WO":"hasd",
            "NO":"qwer"
        },
    ],
    "itemOrder":
        ["WR", "QU", "QA", "WO", "NO"]
}

Вы перебираете список itemOrder и используете их для поиска значений карты.Порядок сохраняется, без клуджей.

Я использовал этот метод много раз.

10 голосов
/ 04 марта 2016

Еще одно хакерское решение с использованием отражения:

JSONObject json = new JSONObject();
Field map = json.getClass().getDeclaredField("map");
map.setAccessible(true);//because the field is private final...
map.set(json, new LinkedHashMap<>());
map.setAccessible(false);//return flag
6 голосов
/ 21 ноября 2013

Apache Wink имеет OrderedJSONObject . Сохраняет порядок при разборе строки.

4 голосов
/ 04 декабря 2014

Просто наткнулся на ту же проблему, я думаю, что окончательное решение, использованное автором, заключалось в использовании пользовательского ContainerFactory:

public static Values parseJSONToMap(String msgData) {
    JSONParser parser = new JSONParser();
    ContainerFactory containerFactory = new ContainerFactory(){
        @Override
        public Map createObjectContainer() {
            return new LinkedHashMap();
        }

        @Override
        public List creatArrayContainer() {
            return null;
        }
    };
    try {
        return (Map<String,Object>)parser.parse(msgData, containerFactory);
    } catch (ParseException e) {
        log.warn("Exception parsing JSON string {}", msgData, e);
    }
    return null;
}  

см. http://juliusdavies.ca/json-simple-1.1.1-javadocs/org/json/simple/parser/JSONParser.html#parse(java.io.Reader,org.json.simple.parser.ContainerFactory)

3 голосов
/ 23 декабря 2010

решаемая.

Я использовал библиотеку JSON.simple отсюда https://code.google.com/p/json-simple/, чтобы прочитать строку JSON, чтобы сохранить порядок ключей, и использовать библиотеку JavaCSV отсюда http://sourceforge.net/projects/javacsv/ для преобразования в формат CSV.

2 голосов
/ 19 января 2015

Я знаю, что это решено, и вопрос задавался давно, но, поскольку я имею дело с подобной проблемой, я хотел бы дать совершенно другой подход к этому:

Для массивов это говорит«Массив - это упорядоченная коллекция значений».в http://www.json.org/ - но объекты («Объект - это неупорядоченный набор пар имя / значение.») не упорядочены.

Интересно, почему этот объект находится в массиве - это подразумеваетпорядок, которого там нет.

{
"items":
[
    {
        "WR":"qwe",
        "QU":"asd",
        "QA":"end",
        "WO":"hasd",
        "NO":"qwer"
    },
]
}

Таким образом, решение будет заключаться в том, чтобы поместить ключи в «реальный» массив и добавить данные в виде объектов к каждому ключу, например:

{
"items":
[
    {"WR": {"data": "qwe"}},
    {"QU": {"data": "asd"}},
    {"QA": {"data": "end"}},
    {"WO": {"data": "hasd"}},
    {"NO": {"data": "qwer"}}
]
}

Так что это подход, который пытается переосмыслить оригинальное моделирование и его намерения.Но я не проверял (и мне интересно), будут ли все задействованные инструменты сохранять порядок этого исходного массива JSON.

1 голос
/ 09 мая 2017

В реальном мире приложение почти всегда будет иметь Java-бин или домен, который нужно сериализовать / десериализовать в / из JSON.Уже упоминалось, что спецификация JSON Object не гарантирует порядок, и любые манипуляции с этим поведением не оправдывают требования.У меня был такой же сценарий в моем приложении, где мне нужно было сохранить порядок только для удобства чтения.Я использовал стандартный способ Джексона для сериализации моего Java-бина в JSON:

Object object = getObject();  //the source java bean that needs conversion
String jsonString = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(object);

Чтобы сделать JSON с упорядоченным набором элементов, я просто использую аннотацию свойства JSON в Java-бине, который я использовал для преобразования.Пример ниже:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","phone","city","id"})
public class SampleBean implements Serializable {
    private int id;
    private String name:
    private String city;
    private String phone;

    //...standard getters and setters
}

используемый выше метод getObject ():

public SampleBean getObject(){
    SampleBean bean  = new SampleBean();
    bean.setId("100");
    bean.setName("SomeName");
    bean.setCity("SomeCity");
    bean.setPhone("1234567890");
    return bean;
}

Вывод отображается в соответствии с аннотацией порядка свойств Json:

{
    name: "SomeName",
    phone: "1234567890",
    city: "SomeCity",
    id: 100
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...