2021年7月7日水曜日

XMLはDOMで扱ったほうがいいわ

XMLからJSONへの変換をしようとしていて、encoding/xml使ったり、XSLで変換かけようかと思ったけど、XSLTは難解で、考え方は分ってきたけど、組みづらい・・・

やはりXMLはDOMで扱わないとダメよ

encoding/xmlとか使っても結局DOM作ろうとしちゃうし、DOM作らないと、XMLの表記方法への対応は出来ない。

たとえば、以下のようにnameが連続していないとき、name項目が一つしかないとき、nameの下に子ノードがあるときなど、汎用的なXMLでは、SAXやxsl的な方法での変換なんて出来ない。

<root>

 <list>

  <name>aiueo

    <special>あいうえお</special>

  </name>

  <name>kakikukeko</name>

  <URL>http://www.blablabla.com</URL>

  <name>sasisuseso</name>

 </list>

</root>

SAXやXSLは、きまった形のXMLでない対応できない。

この考え方が、xslを使えない奴にしてしまっている。

ていうか、xslで出来ることがHTML変換くらいなものなのだが、

JavaScript系のエンジニアは、JSONでそのまま扱いたいからxslなんて勉強しない。

そして余計にXMLが嫌われていく

xslもオブジェクト指向に毒されてしまっているのだと思う。


golangやってると、オブジェクト指向なんてどうでもよくなる。

(まったくオブジェクト指向なんて悪い夢を見ていたようだよ)

話を戻して、JSONへの変換元となるXMLは、形が無限の種類のXMLなので、SAXやXSLでは絶対に無理だ。

例えば上記の場合、name以下は全部強制的に配列にしたい。

nameの途中にURLタグが出てきちゃうので、nameで要素をまとめてJSON配列にしないといけない。

これは、encoding/xmlのtokenじゃ無理

一度読み込んでまとめ、ソートするなど工夫が必要だ。

つまりそれってDOMだよ


強制配列を実現するために全体をDATAタグで括って、プロパティでForceArrayを指定して変換できるように考えていた。

まず、このアトリビュートの取得をして、複数のXPATHをXSLのVariableに配列としてSplitしようかと思ったのだけど、xsl2.0じゃないとtokenize使えないし、xsl2.0なんて誰も実装してないし、SAXXONくらいだし、俺Java嫌いだし絶対に使わねー

go-xmldomでxml2json作ろうかと思ってまずはxmldomのテストプログラムを動かしてみた。

名前はxml2json.goだけど、機能はxml2jsonじゃありません。

単にテストプログラムをそのまま動かしただけです

Qiitaみたいですみません

===xml2json.go===

package xml2json

import (
    "fmt"

    xmldom "github.com/subchen/go-xmldom"
)

func xml2json() {
    xml := `<testsuite tests="2" failures="0" time="0.009" name="github.com/subchen/go-xmldom">
    <testcase classname="go-xmldom" name="ExampleParseXML" time="0.004"></testcase>
    <testcase classname="go-xmldom" name="ExampleParse" time="0.005"></testcase>
</testsuite>`

    doc := xmldom.Must(xmldom.ParseXML(xml))
    root := doc.Root

    name := root.GetAttributeValue("name")
    time := root.GetAttributeValue("time")
    fmt.Printf("testsuite: name=%v, time=%v\n", name, time)

    for _, node := range root.GetChildren("testcase") {
        name := node.GetAttributeValue("name")
        time := node.GetAttributeValue("time")
        fmt.Printf("testcase: name=%v, time=%v\n", name, time)
    }
}


んで、テストになってないテストプログラム

package xml2json

import "testing"

func TestXml2JSON(t *testing.T) {
    xml2json()
}



go test -vで動かすと以下の様に出てくる

=== RUN   TestXml2JSON

testsuite: name=github.com/subchen/go-xmldom, time=0.009

testcase: name=ExampleParseXML, time=0.004

testcase: name=ExampleParse, time=0.005

--- PASS: TestXml2JSON (0.00s)

PASS

ok      xml2json        0.004s





2021年7月5日月曜日

XMLからJSONへ

簡単に考えていたのだが、XML表記をそのままJSONへ変換することは出来ない。

簡単に言うと2つ問題がある。

1.XMLから配列への扱い

入力XML(NAMEが1つのとき)

<LIST>
 <NAME>あいうえお</NAME>
 <NAME>かきくけこ</NAME>
 </LIST>

出力JSON(NAMEが1つのとき)

{
    "LIST": {
        "NAME": "あいうえお"
    }
}

入力XML(NAMEが1つ以上とのき)

<LIST>
 <NAME>あいうえお</NAME>
 <NAME>かきくけこ</NAME>
 </LIST>

出力JSON(NAMEが1つ以上のとき)

{
    "LIST": {
        "NAME": [
            "あいうえお",
            "かきくけこ"
        ]
    }
}

これは非常にわかりやすい

XMLのNAMEが1件なら配列にはならないのだが、

1件以上あれば鍵カッコで囲われて配列になっている。

なので、XMLの特定のタグについては、

強制配列化を行いたいという要望があちこちのXML2JSONで散見されている。

2.アトリビュート有り無し混在

続いて、アトリビュートの扱いだ。

入力(アトリビュート有り無し混在XML)

<LIST>
 <NAME ID='1' IMPORTANCE='20'>あいうえお</NAME>
 <NAME>かきくけこ</NAME>
</LIST>

出力JSON(アトリビュート有り無し部のアクセスの仕方が違ってしまう

{
    "LIST": {
        "NAME": [
            {
                "-ID": "1",
                "-IMPORTANCE": "20",
                "#text": "あいうえお"
            },
            "かきくけこ"
        ]
    }
}

これ、非常にまずい、あいうえおは#textになってて、かきくけこは普通に配列に入っている。ついでにXMLのアトリビュート名にはハイフンがプレフィックスとしてついている。

しかし、XMLからJSONへの変換の仕方としてはこれが一般的なのだ。


上記1の問題から、タグごとにアトリビュートでFORCEARRAYの設定を付けたら配列になるような仕様にするべきと考える。


上記問題2の結果から同じ階層で同じタグ名で、アトリビュート有り無し混在したXMLはJSONへの変換に用いてはならないことになる。


んで、これら2つの問題を考慮したうえで、

XMLからJSONへの変換をもう一度考えてみたが、

やり方として別にgolangでやる必要なんかなかったのでは?

そもそもXMLなんだから、なんでXSLTしないの?

という事に気が付いた。

んで、今色々と試行錯誤中

2021年7月1日木曜日

goxml2jsonのバグが修正されないので、自前で作ろうとまずはXMLを列挙する部分を作った

goxml2jsonの強制配列化はバグがある

詳細はこちら

最初の1行にしか強制配列化が適用されない。

書き込みの最後にアトリビュートでどうたら書かれているけど、

これはただの提案であり、XML的には単なるアトリビュートなので、全く意味のない書き込みだ。

しかも、指定した場所以降、すべて配列化されてしまう。

そして、配列化する場所は一か所しか指定できない。


仕方ないので、自前で作ろうと思う。

とはいえ、encoding/xmlは使う。

こちらにPlayGroundの例を置いておく

まずは任意のXMLを列挙する部分を作った。

encoding/xmlを使うときに、なぜか皆さん、構造体を作って割り当てるやり方しかしていない。

JSONなんざ文字列なので、XMLを列挙しながら対応していけばいいんだよ

まずは列挙だけしたからあとは勝手に作ればいい

2021年4月7日水曜日

JSONって・・・

 手打ちでJSONの戻り値を入れようと思ったのだけど、

まちがってたわ

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse


ここで手打ちしたものを試して、なんとなく仕組みがわかってきた。

fmt.Fprint(w, "{\"status\": 200, \"result\": \"ok\"}")

最初statusやresultはクォーテーションで囲っていなかったのだが、

上記のようにクォートしないとダメだった。

こんなもんで結構な時間ハマった・・・

アフォか

2021年4月2日金曜日

SEDNAでHTMLのインサート

SEDNAでHTMLをインサートする際の動きが難しいので考えを整理する。
やって見たことは以下2点

(1)CDATAセクションでくくる
   se_expでエクスポートしてみたら、CDATAは消えていて、
   HTMLタグが&lt;などでエスケープされていた。
(2)前もってHTMLを加工してインサート
   前もってHTMLを加工して、改行を&#10に変え1行にし、
   HTMLタグも&lt;などに変えて、変数に入れたのち、
   その変数を使ってインサートし、se_expでエクスポートしてみたら、
   1とほぼ変わらなかった。
結論
  (2)により改行がなくなるので、XQueryで変数への代入が可能になる
  しかし(1)もやらないと、XQueryでは結局改行が展開されてしまうので、
  複数行になってしまう。
  その際CDATAセクションにしておかないと、Syntaxエラーになってしまう。
  結局上記(1)と(2)両方用いて対応した。

2021年4月1日木曜日

golang で バックエンドのCORS処理

CORSてのがあって詳しくはココ参照
ブラウザからリクエストが来るときは、最初のメソッドがPUTやPOSTなどではなく、OPTIONSになるらしい。
OPTIONSの時は以下のようなヘッダをセットして返してあげると、続けて本番のMethodが飛んでくる。
この流れをpreflightと言うらしい それで実際の動きはどうなるかというとこんな感じ

2021年3月30日火曜日

Sedna の Insert文でHTMLタグを含む情報をインサート

TITLEは普通のテキストだからいいとして、 PAGE情報はHTMLだのJavaScriptだの入るのでCDATAセクションにしないとダメそうだ。

2021年3月25日木曜日

sednaで複数の更新を一気に行うための下準備をsedna側クエリとpugixmlでやろうとしてる

ここ数日色々調べてできたところまでまとめてみた
「sednaで複数の更新を一気に行うための下準備をsedna側クエリとpugixmlでやろうとしてる」
フロントエンドからの要求に基づいて、複数行の更新をしようとしたときに、Sednaにはその機能が無いため、コントローラ側で実装することになるわけだ。
普通のDBに例えると、
「フロントエンドから、データベースのカラムを増やして、そこに既存のテキストをとりあえずインサートしたい」
という要求にこたえるための処理を書いている。
pugixmlが早いというので、それをXML処理に採用した。
sedna側のクエリで特定のタグはCDATAセクションに指定できることがわかったので、XML文を含むテキスト(sednaに渡す命令)全体をCDATAセクションで囲んだ。
これをしないと、pugiwikiはテキスト中のXML文をテキストとして処理せず、ノード扱いしてしまう。
これからの流れとしては、
xml処理はC++で書くため、Swigを使ってGolang用にラッパーを作り、golangから呼び出すようにする。


これで、ようやく、複数のアップデートをするための下準備ができた。
出てきた文字列をSednaでExecuteすればよい
実際はencodeint/xmlで出来てたんだけど、
これが遅いということなので、pugixmlを使ってC++で処理して、Swig使ったりしてGoで動かそうとしているところ
色々大変である・・・
ところでCDATAセクションをネストするときは内側の]]>を]]]]><![CDATA[>に変ええないといけないらしい。
知らんかった 上記を先ほどのソースで処理すると このようにpageの中でネストしていた部分がきれいに剥がれ落ちてくれ、、、???
あれ?変なところに改行入ってるな?
なんだろう?
これはさすがにバグですかね?
いずれにしろ、XMLの中にHTMLを入れるのは中にスクリプトやXMLのデータまで入っていたりすることも考慮しないといけないし、
そのままCDATAに括って入れるのはダメだろうな
ということで、ネストするのは/sedna/procs/procだけにして、
sednaのインサート文で入れる中身によって加工する必要がある。
プレーンなテキストを入れてもいい場合はそのままでいいが
HTMLを入れたいなら、一度全体を圧縮して、全体をBase64にしていれるべきだと思う。
結果、HTML内に埋め込まれたイメージなどはBase64したものがZIP圧縮されて再度Base64という余計なことになってしまうが
まぁ仕方ないよな?
ためしに、出力された余計な改行が入ったUPDATE用Sednaクエリーをse_termで実行したらやはりエラーになった。
pugixml側を直さないとダメだな
違った・・・
これの出力結果(入力するXMLはPROCの数を増やしてある)
つまり、改行を入れていたのは自分のプログラムなんだけど、
ループの回数がネストされたCDATAの数だけ増えてしまうようだ。
別の取得方法をしないとだめか?
いや、これはnext_siblingを取れば行けそうだ・・・


やっと色々納得できて、まともに出るようになった。

2021年3月24日水曜日

UbuntuへのSwigインストール

なんでやねん?
git branch みたらちゃんとmasterなんですけど?
READMEみたら、4.1.0って書いてあるのに、どういうこと?
gitメンテしてないの?
しかもSourceForgeの方は4.0.2だぜ?
おいおい、ちゃんとしようよ? てか、Releaseのタグがあるわ、402落としなおして上記再実行し、
インストール完了
バージョン確認したら4.0.1????
ふざけんな!

Pugixml使ってみただけの内容

一番時間がかかったのがsyntaxhighlighterだったりする。
これやらないとプログラムネタの記事書いてらんね

2021年3月19日金曜日

OpenCVの環境構築

  •  OpenCVのインストール手順

>sudo apt install -y g++
>sudo apt install -y clang
>sudo apt install libboost-dev
>sudo apt install -y cmake
>sudo apt install -y make
>sudo apt install -y wget unzip
>git clone https://github.com/opencv/opencv.git
>git -C opencv checkout master
>mkdir -p build && cd build
>cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local -DOPENCV_GENERATE_PKGCONFIG=ON ../opencv
  • >make -j4


  • OpenCV のmake結果を確認

>ls bin
>ls lib
>ls OpenCVConfig*.cmake
>ls OpenCVModules.cmake

  • OpenCVをインストール

>sudo make install

  • path設定(設定場所はどこでもよいけど/etc/profileに書いてみる。golangの環境変数もこちらに書いてしまってます。)

>sudo vi /etc/profile
put following settings at the last of profile
export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:/usr/local/sedna/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

  • OpenCV動作確認

=== test.cpp ===
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>
using namespace cv;

int main(void){
        Mat image = imread("xxxx.bmp");
        rectangle(image, Rect(0,0,100,100),Scalar(0,0,0),FILLED);
        imwrite("test.jpg",image);

        return 0;
}
==== コンパイル ====
g++ test.cpp -std=c++11 `pkg-config --cflags --libs opencv4`
a.out will be generated.

=== xxxx.bmp を準備してa.out を実行 ===
xxx.bmp を事前に用意してください。中身はかならずしもBMPじゃなくても良く、JPGやBMPでも構わないので、リネームして用意してください。
(例:mv aaa.png xxxx.bmp)

./a.out
test.jpg ができます。(左上が黒塗りの■になってるはずです。)