Сохранять порядок ключей JSON во время преобразования JSON в CSV

Я использую библиотеку 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

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

Ответ 1

решаемые.

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

Ответ 2

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

В 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" должен использоваться каким-либо другим инструментом, это будет проблематично. Действительно, эта проблема будет возникать снова и снова...

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

UPDATE

Также стоит прочитать, что говорит

Ответ 3

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

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

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

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

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

Ответ 4

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

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

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

Итак, прежде чем строить конструкцию json object 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. Надеюсь, это поможет кому-то.

Ответ 5

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

Пример:

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

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

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

Ответ 6

Другое хакерское решение, использующее отражение:

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

Ответ 7

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

Ответ 8

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

Для массивов он говорит: "Массив - это упорядоченный набор значений". на 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.

Ответ 9

Просто наткнувшись на ту же проблему, я считаю, что окончательное решение, используемое автором, состояло в использовании пользовательского 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)

Ответ 10

Наиболее безопасным способом является, вероятно, переопределяющий метод ключей, который используется для генерации вывода:

new JSONObject(){
  @Override
  public Iterator keys(){
    TreeSet<Object> sortedKeys = new TreeSet<Object>();
    Iterator keys = super.keys();
    while(keys.hasNext()){
      sortedKeys.add(keys.next());
    }
    return sortedKeys.iterator();
  }
};

Ответ 11

patchFor (answer @gary):

$ git diff JSONObject.java                                                         
diff --git a/JSONObject.java b/JSONObject.java
index e28c9cd..e12b7a0 100755
--- a/JSONObject.java
+++ b/JSONObject.java
@@ -32,7 +32,7 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Enumeration;
-import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Map;
@@ -152,7 +152,9 @@ public class JSONObject {
      * Construct an empty JSONObject.
      */
     public JSONObject() {
-        this.map = new HashMap<String, Object>();
+//      this.map = new HashMap<String, Object>();
+        // I want to keep order of the given data:
+        this.map = new LinkedHashMap<String, Object>();
     }

     /**
@@ -243,7 +245,7 @@ public class JSONObject {
      * @throws JSONException
      */
     public JSONObject(Map<String, Object> map) {
-        this.map = new HashMap<String, Object>();
+        this.map = new LinkedHashMap<String, Object>();
         if (map != null) {
             Iterator<Entry<String, Object>> i = map.entrySet().iterator();
             while (i.hasNext()) {

Ответ 12

Вы можете использовать следующий код для пользовательской сериализации ORDERED и десериализации JSON Array (в этом примере предполагается, что вы заказываете строки, но можете применяться ко всем типам):

Сериализация

JSONArray params = new JSONArray();
int paramIndex = 0;

for (String currParam : mParams)
{
    JSONObject paramObject = new JSONObject();
    paramObject.put("index", paramIndex);
    paramObject.put("value", currParam);

    params.put(paramObject);
    ++paramIndex;
}

json.put("orderedArray", params);

Десериализация

JSONArray paramsJsonArray = json.optJSONArray("orderedArray");
if (null != paramsJsonArray)
{
    ArrayList<String> paramsArr = new ArrayList<>();
    for (int i = 0; i < paramsJsonArray.length(); i++)
    {
        JSONObject param = paramsJsonArray.optJSONObject(i);
        if (null != param)
        {
            int paramIndex = param.optInt("index", -1);
            String paramValue = param.optString("value", null);

            if (paramIndex > -1 && null != paramValue)
            {
                paramsArr.add(paramIndex, paramValue);
            }
        }
    }
}

Ответ 13

Ваш пример:

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

добавить элемент "itemorder"

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

Этот код генерирует желаемый результат без строки заголовка столбца:

JSONObject output = new JSONObject(json);
JSONArray docs = output.getJSONArray("data");
JSONArray names = output.getJSONArray("itemOrder");
String csv = CDL.toString(names,docs);

Ответ 14

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

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

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

@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
}

Ответ 15

Протестировано решение подмигивания и работает нормально:

@Test
public void testJSONObject() {
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    System.out.println(jsonObject.toString());
    assertTrue(jsonObject.toString().startsWith("{\"xxx\":"));
}

@Test
public void testWinkJSONObject() throws JSONException {
    org.apache.wink.json4j.JSONObject jsonObject = new OrderedJSONObject();
    jsonObject.put("bbb", "xxx");
    jsonObject.put("ccc", "xxx");
    jsonObject.put("aaa", "xxx");
    jsonObject.put("xxx", "xxx");
    assertEquals("{\"bbb\":\"xxx\",\"ccc\":\"xxx\",\"aaa\":\"xxx\",\"xxx\":\"xxx\"}", jsonObject.toString());
}